diff --git a/License.md b/License.md new file mode 100644 index 0000000..299b3f9 --- /dev/null +++ b/License.md @@ -0,0 +1,100 @@ +## APPLE PUBLIC SOURCE LICENSE +## Version 2.0 - August 6, 2003 + +Please read this License carefully before downloading this software. By downloading or using this software, you are agreeing to be bound by the terms of this License. If you do not or cannot agree to the terms of this License, please do not download or use the software. + +1\. General; Definitions. This License applies to any program or other work which Apple Computer, Inc. ("Apple") makes publicly available and which contains a notice placed by Apple identifying such program or work as "Original Code" and stating that it is subject to the terms of this Apple Public Source License version 2.0 ("License"). As used in this License: + +1.1 "Applicable Patent Rights" mean: (a) in the case where Apple is the grantor of rights, (i) claims of patents that are now or hereafter acquired, owned by or assigned to Apple and (ii) that cover subject matter contained in the Original Code, but only to the extent necessary to use, reproduce and/or distribute the Original Code without infringement; and (b) in the case where You are the grantor of rights, (i) claims of patents that are now or hereafter acquired, owned by or assigned to You and (ii) that cover subject matter in Your Modifications, taken alone or in combination with Original Code. + +1.2 "Contributor" means any person or entity that creates or contributes to the creation of Modifications. + +1.3 "Covered Code" means the Original Code, Modifications, the combination of Original Code and any Modifications, and/or any respective portions thereof. + +1.4 "Externally Deploy" means: (a) to sublicense, distribute or otherwise make Covered Code available, directly or indirectly, to anyone other than You; and/or (b) to use Covered Code, alone or as part of a Larger Work, in any way to provide a service, including but not limited to delivery of content, through electronic communication with a client other than You. + +1.5 "Larger Work" means a work which combines Covered Code or portions thereof with code not governed by the terms of this License. + +1.6 "Modifications" mean any addition to, deletion from, and/or change to, the substance and/or structure of the Original Code, any previous Modifications, the combination of Original Code and any previous Modifications, and/or any respective portions thereof. When code is released as a series of files, a Modification is: (a) any addition to or deletion from the contents of a file containing Covered Code; and/or (b) any new file or other representation of computer program statements that contains any part of Covered Code. + +1.7 "Original Code" means (a) the Source Code of a program or other work as originally made available by Apple under this License, including the Source Code of any updates or upgrades to such programs or works made available by Apple under this License, and that has been expressly identified by Apple as such in the header file(s) of such work; and (b) the object code compiled from such Source Code and originally made available by Apple under this License. + +1.8 "Source Code" means the human readable form of a program or other work that is suitable for making modifications to it, including all modules it contains, plus any associated interface definition files, scripts used to control compilation and installation of an executable (object code). + +1.9 "You" or "Your" means an individual or a legal entity exercising rights under this License. For legal entities, "You" or "Your" includes any entity which controls, is controlled by, or is under common control with, You, where "control" means (a) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (b) ownership of fifty percent (50%) or more of the outstanding shares or beneficial ownership of such entity. + +2\. Permitted Uses; Conditions & Restrictions. Subject to the terms and conditions of this License, Apple hereby grants You, effective on the date You accept this License and download the Original Code, a world-wide, royalty-free, non-exclusive license, to the extent of Apple's Applicable Patent Rights and copyrights covering the Original Code, to do the following: + +2.1 Unmodified Code. You may use, reproduce, display, perform, internally distribute within Your organization, and Externally Deploy verbatim, unmodified copies of the Original Code, for commercial or non-commercial purposes, provided that in each instance: + +(a) You must retain and reproduce in all copies of Original Code the copyright and other proprietary notices and disclaimers of Apple as they appear in the Original Code, and keep intact all notices in the Original Code that refer to this License; and + +(b) You must include a copy of this License with every copy of Source Code of Covered Code and documentation You distribute or Externally Deploy, and You may not offer or impose any terms on such Source Code that alter or restrict this License or the recipients' rights hereunder, except as permitted under Section 6. + +2.2 Modified Code. You may modify Covered Code and use, reproduce, display, perform, internally distribute within Your organization, and Externally Deploy Your Modifications and Covered Code, for commercial or non-commercial purposes, provided that in each instance You also meet all of these conditions: + +(a) You must satisfy all the conditions of Section 2.1 with respect to the Source Code of the Covered Code; + +(b) You must duplicate, to the extent it does not already exist, the notice in Exhibit A in each file of the Source Code of all Your Modifications, and cause the modified files to carry prominent notices stating that You changed the files and the date of any change; and + +(c) If You Externally Deploy Your Modifications, You must make Source Code of all Your Externally Deployed Modifications either available to those to whom You have Externally Deployed Your Modifications, or publicly available. Source Code of Your Externally Deployed Modifications must be released under the terms set forth in this License, including the license grants set forth in Section 3 below, for as long as you Externally Deploy the Covered Code or twelve (12) months from the date of initial External Deployment, whichever is longer. You should preferably distribute the Source Code of Your Externally Deployed Modifications electronically (e.g. download from a web site). + +2.3 Distribution of Executable Versions. In addition, if You Externally Deploy Covered Code (Original Code and/or Modifications) in object code, executable form only, You must include a prominent notice, in the code itself as well as in related documentation, stating that Source Code of the Covered Code is available under the terms of this License with information on how and where to obtain such Source Code. + +2.4 Third Party Rights. You expressly acknowledge and agree that although Apple and each Contributor grants the licenses to their respective portions of the Covered Code set forth herein, no assurances are provided by Apple or any Contributor that the Covered Code does not infringe the patent or other intellectual property rights of any other entity. Apple and each Contributor disclaim any liability to You for claims brought by any other entity based on infringement of intellectual property rights or otherwise. As a condition to exercising the rights and licenses granted hereunder, You hereby assume sole responsibility to secure any other intellectual property rights needed, if any. For example, if a third party patent license is required to allow You to distribute the Covered Code, it is Your responsibility to acquire that license before distributing the Covered Code. + +3\. Your Grants. In consideration of, and as a condition to, the licenses granted to You under this License, You hereby grant to any person or entity receiving or distributing Covered Code under this License a non-exclusive, royalty-free, perpetual, irrevocable license, under Your Applicable Patent Rights and other intellectual property rights (other than patent) owned or controlled by You, to use, reproduce, display, perform, modify, sublicense, distribute and Externally Deploy Your Modifications of the same scope and extent as Apple's licenses under Sections 2.1 and 2.2 above. + +4\. Larger Works. You may create a Larger Work by combining Covered Code with other code not governed by the terms of this License and distribute the Larger Work as a single product. In each such instance, You must make sure the requirements of this License are fulfilled for the Covered Code or any portion thereof. + +5\. Limitations on Patent License. Except as expressly stated in Section 2, no other patent rights, express or implied, are granted by Apple herein. Modifications and/or Larger Works may require additional patent licenses from Apple which Apple may grant in its sole discretion. + +6\. Additional Terms. You may choose to offer, and to charge a fee for, warranty, support, indemnity or liability obligations and/or other rights consistent with the scope of the license granted herein ("Additional Terms") to one or more recipients of Covered Code. However, You may do so only on Your own behalf and as Your sole responsibility, and not on behalf of Apple or any Contributor. You must obtain the recipient's agreement that any such Additional Terms are offered by You alone, and You hereby agree to indemnify, defend and hold Apple and every Contributor harmless for any liability incurred by or claims asserted against Apple or such Contributor by reason of any such Additional Terms. + +7\. Versions of the License. Apple may publish revised and/or new versions of this License from time to time. Each version will be given a distinguishing version number. Once Original Code has been published under a particular version of this License, You may continue to use it under the terms of that version. You may also choose to use such Original Code under the terms of any subsequent version of this License published by Apple. No one other than Apple has the right to modify the terms applicable to Covered Code created under this License. + +8\. NO WARRANTY OR SUPPORT. The Covered Code may contain in whole or in part pre-release, untested, or not fully tested works. The Covered Code may contain errors that could cause failures or loss of data, and may be incomplete or contain inaccuracies. You expressly acknowledge and agree that use of the Covered Code, or any portion thereof, is at Your sole and entire risk. THE COVERED CODE IS PROVIDED "AS IS" AND WITHOUT WARRANTY, UPGRADES OR SUPPORT OF ANY KIND AND APPLE AND APPLE'S LICENSOR(S) (COLLECTIVELY REFERRED TO AS "APPLE" FOR THE PURPOSES OF SECTIONS 8 AND 9) AND ALL CONTRIBUTORS EXPRESSLY DISCLAIM ALL WARRANTIES AND/OR CONDITIONS, EXPRESS OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES AND/OR CONDITIONS OF MERCHANTABILITY, OF SATISFACTORY QUALITY, OF FITNESS FOR A PARTICULAR PURPOSE, OF ACCURACY, OF QUIET ENJOYMENT, AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. APPLE AND EACH CONTRIBUTOR DOES NOT WARRANT AGAINST INTERFERENCE WITH YOUR ENJOYMENT OF THE COVERED CODE, THAT THE FUNCTIONS CONTAINED IN THE COVERED CODE WILL MEET YOUR REQUIREMENTS, THAT THE OPERATION OF THE COVERED CODE WILL BE UNINTERRUPTED OR ERROR-FREE, OR THAT DEFECTS IN THE COVERED CODE WILL BE CORRECTED. NO ORAL OR WRITTEN INFORMATION OR ADVICE GIVEN BY APPLE, AN APPLE AUTHORIZED REPRESENTATIVE OR ANY CONTRIBUTOR SHALL CREATE A WARRANTY. You acknowledge that the Covered Code is not intended for use in the operation of nuclear facilities, aircraft navigation, communication systems, or air traffic control machines in which case the failure of the Covered Code could lead to death, personal injury, or severe physical or environmental damage. + +9\. LIMITATION OF LIABILITY. TO THE EXTENT NOT PROHIBITED BY LAW, IN NO EVENT SHALL APPLE OR ANY CONTRIBUTOR BE LIABLE FOR ANY INCIDENTAL, SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES ARISING OUT OF OR RELATING TO THIS LICENSE OR YOUR USE OR INABILITY TO USE THE COVERED CODE, OR ANY PORTION THEREOF, WHETHER UNDER A THEORY OF CONTRACT, WARRANTY, TORT (INCLUDING NEGLIGENCE), PRODUCTS LIABILITY OR OTHERWISE, EVEN IF APPLE OR SUCH CONTRIBUTOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES AND NOTWITHSTANDING THE FAILURE OF ESSENTIAL PURPOSE OF ANY REMEDY. SOME JURISDICTIONS DO NOT ALLOW THE LIMITATION OF LIABILITY OF INCIDENTAL OR CONSEQUENTIAL DAMAGES, SO THIS LIMITATION MAY NOT APPLY TO YOU. In no event shall Apple's total liability to You for all damages (other than as may be required by applicable law) under this License exceed the amount of fifty dollars ($50.00). + +10\. Trademarks. This License does not grant any rights to use the trademarks or trade names "Apple", "Apple Computer", "Mac", "Mac OS", "QuickTime", "QuickTime Streaming Server" or any other trademarks, service marks, logos or trade names belonging to Apple (collectively "Apple Marks") or to any trademark, service mark, logo or trade name belonging to any Contributor. You agree not to use any Apple Marks in or as part of the name of products derived from the Original Code or to endorse or promote products derived from the Original Code other than as expressly permitted by and in strict compliance at all times with Apple's third party trademark usage guidelines which are posted at http://www.apple.com/legal/guidelinesfor3rdparties.html. + +11\. Ownership. Subject to the licenses granted under this License, each Contributor retains all rights, title and interest in and to any Modifications made by such Contributor. Apple retains all rights, title and interest in and to the Original Code and any Modifications made by or on behalf of Apple ("Apple Modifications"), and such Apple Modifications will not be automatically subject to this License. Apple may, at its sole discretion, choose to license such Apple Modifications under this License, or on different terms from those contained in this License or may choose not to license them at all. + +12\. Termination. + +12.1 Termination. This License and the rights granted hereunder will terminate: + +(a) automatically without notice from Apple if You fail to comply with any term(s) of this License and fail to cure such breach within 30 days of becoming aware of such breach; + +(b) immediately in the event of the circumstances described in Section 13.5(b); or + +(c) automatically without notice from Apple if You, at any time during the term of this License, commence an action for patent infringement against Apple; provided that Apple did not first commence an action for patent infringement against You in that instance. + +12.2 Effect of Termination. Upon termination, You agree to immediately stop any further use, reproduction, modification, sublicensing and distribution of the Covered Code. All sublicenses to the Covered Code which have been properly granted prior to termination shall survive any termination of this License. Provisions which, by their nature, should remain in effect beyond the termination of this License shall survive, including but not limited to Sections 3, 5, 8, 9, 10, 11, 12.2 and 13. No party will be liable to any other for compensation, indemnity or damages of any sort solely as a result of terminating this License in accordance with its terms, and termination of this License will be without prejudice to any other right or remedy of any party. + +13\. Miscellaneous. + +13.1 Government End Users. The Covered Code is a "commercial item" as defined in FAR 2.101. Government software and technical data rights in the Covered Code include only those rights customarily provided to the public as defined in this License. This customary commercial license in technical data and software is provided in accordance with FAR 12.211 (Technical Data) and 12.212 (Computer Software) and, for Department of Defense purchases, DFAR 252.227-7015 (Technical Data -- Commercial Items) and 227.7202-3 (Rights in Commercial Computer Software or Computer Software Documentation). Accordingly, all U.S. Government End Users acquire Covered Code with only those rights set forth herein. + +13.2 Relationship of Parties. This License will not be construed as creating an agency, partnership, joint venture or any other form of legal association between or among You, Apple or any Contributor, and You will not represent to the contrary, whether expressly, by implication, appearance or otherwise. + +13.3 Independent Development. Nothing in this License will impair Apple's right to acquire, license, develop, have others develop for it, market and/or distribute technology or products that perform the same or similar functions as, or otherwise compete with, Modifications, Larger Works, technology or products that You may develop, produce, market or distribute. + +13.4 Waiver; Construction. Failure by Apple or any Contributor to enforce any provision of this License will not be deemed a waiver of future enforcement of that or any other provision. Any law or regulation which provides that the language of a contract shall be construed against the drafter will not apply to this License. + +13.5 Severability. (a) If for any reason a court of competent jurisdiction finds any provision of this License, or portion thereof, to be unenforceable, that provision of the License will be enforced to the maximum extent permissible so as to effect the economic benefits and intent of the parties, and the remainder of this License will continue in full force and effect. (b) Notwithstanding the foregoing, if applicable law prohibits or restricts You from fully and/or specifically complying with Sections 2 and/or 3 or prevents the enforceability of either of those Sections, this License will immediately terminate and You must immediately discontinue any use of the Covered Code and destroy all copies of it that are in your possession or control. + +13.6 Dispute Resolution. Any litigation or other dispute resolution between You and Apple relating to this License shall take place in the Northern District of California, and You and Apple hereby consent to the personal jurisdiction of, and venue in, the state and federal courts within that District with respect to this License. The application of the United Nations Convention on Contracts for the International Sale of Goods is expressly excluded. + +13.7 Entire Agreement; Governing Law. This License constitutes the entire agreement between the parties with respect to the subject matter hereof. This License shall be governed by the laws of the United States and the State of California, except that body of California law concerning conflicts of law. + +Where You are located in the province of Quebec, Canada, the following clause applies: The parties hereby confirm that they have requested that this License and all related documents be drafted in English. Les parties ont exige que le present contrat et tous les documents connexes soient rediges en anglais. + +EXHIBIT A. + +"Portions Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved. + +This file contains Original Code and/or Modifications of Original Code as defined in and that are subject to the Apple Public Source License Version 2.0 (the 'License'). You may not use this file except in compliance with the License. Please obtain a copy of the License at http://www.opensource.apple.com/apsl/ and read it before using this file. + +The Original Code and all software distributed under the License are distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. Please see the License for the specific language governing rights and limitations under the License." diff --git a/README.md b/README.md index 50841a2..a440e88 100644 --- a/README.md +++ b/README.md @@ -1,28 +1,594 @@ -# OS-X-ALPS-DRIVER - -This driver is based on Rehabman's VoodooPS2 driver for macOS, Linux 4.7, and bpedman's earlier work. V7 support was ported by coolstarorg. - -The aim of this driver is to improve the usability of ALPS TouchPads in macOS. - -Driver Features: -- Supports ALPS hardware versions V1-V5, V7 -- Supports Mac OS 10.6 to 10.12 -- 1-finger tapping. -- Side (edge) scrolling (Vertical with inertia, and Horizontal). -- 2-finger tap for right click. -- 2-finger scrolling (vertical and horizontal with inertia). -- 3 and 4-finger gestures (V3+, check the log to find out your hw version). -- Pointer acceleration and smoothing. -- Trackstick (movement and scrolling). - -========================================== - -Changes: -- Touchpad initialization, packet decoding and processing updated Linux 4.7 -- Bitmap processing overhauled. Can now provide coordinates for 2 fingers simultaneously. -- Added new device ids for better compatibility. -- Remove hardcoded variables allowing userspace (plist) settings to apply. -- Painstakingly optimized default settings to provide the best possible experience. -- Improve pointer motion calculation, mode switching,… etc. -- Lots of code cleanups, refactoring. -- Clean up the IOLog. +## Modified VoodooPS2Controller by RehabMan + + +### How to Install: + +Please read and follow the important instructions for installing in the wiki: + +https://github.com/RehabMan/OS-X-Voodoo-PS2-Controller/wiki/How-to-Install + + +### Downloads: + +Downloads are available on Bitbucket: + +https://bitbucket.org/RehabMan/os-x-voodoo-ps2-controller/downloads + +Note: Archived (old) downloads are available on Google Code: + +https://code.google.com/p/os-x-voodoo-ps2-controller/downloads/list + + +### Build Environment + +My build environment is currently Xcode 6.1, using SDK 10.8, targeting OS X 10.6. + +No other build environment is supported. + + +### 32-bit Builds + +Currently, builds are provided only for 64-bit systems. 32-bit/64-bit FAT binaries are not provided. But you can build your own should you need them. I do not test 32-bit, and there may be times when the repo is broken with respect to 32-bit builds, but I do check on major releases to see if the build still works for 32-bit. + +Here's how to build 32-bit (universal): + +- xcode 4.6.3 +- open VoodooPS2Controller.xcodeproj +- click on VoodooPS2Controller at the top of the project tree +- select VoodooPS2Controller under Project +- change Architectures to 'Standard (32/64-bit Intel)' + +probably not necessary, but a good idea to check that the targets don't have overrides: +- multi-select all the Targets +- check/change Architectures to 'Standard (32/64-bit Intel)' +- build (either w/ menu or with make) + +Or, if you have the command line tools installed, just run: + +- For FAT binary (32-bit and 64-bit in one binary) +make BITS=3264 + +- For 32-bit only +make BITS=32 + + +### Source Code: + +The source code is maintained at the following sites: + +https://code.google.com/p/os-x-voodoo-ps2-controller/ + +https://github.com/RehabMan/OS-X-Voodoo-PS2-Controller + +https://bitbucket.org/RehabMan/os-x-voodoo-ps2-controller + + +### Feedback: + +Please use the following threads on tonymacx86.com for feedback, questions, and help: + +http://www.tonymacx86.com/hp-probook/75649-new-voodoops2controller-keyboard-trackpad.html#post468941 +http://www.tonymacx86.com/mountain-lion-laptop-support/87182-new-voodoops2controller-keyboard-trackpad-clickpad-support.html#post538365 + + +### Fun Facts: + +While implementing the "just for fun" feature in the keyboard driver where Ctrl+Alt+Del maps to the power key (for selection of Restart, Sleep, Shutdown), I discovered that if you invoke this function with the Ctrl and Alt (Command) keys down, the system will do an abrupt and unsafe restart. You can verify this yourself by holding down the Ctrl and Alt keys while pressing the actual power button. + + +### Known issues: + +- Very rarely, both the keyboard and trackpad are not working after a fresh boot or after sleep, even on systems where this is normally not a problem. As of the v1.7.15a release this problem appears to be solved. Time will tell. + +- Very rarely, the keyboard/trackpad may become unresponsive or a key may repeat indefinitely. As of the v1.7.15a release this problem appears to be solved. Time will tell. + +Note: often times you will see either of the two problems mentioned above right after installing. Generally, another reboot and/or repair permissions & rebuild caches will fix the problem. + + +### Change Log: + +2015-05-02 v1.8.15 + +- Fix a problem with phantom key event e027 on certain ProBook laptops causing spurious/random fnkeys toggle + +- Allow for discrete fnkeys toggle and discrete trackpad toggle setup + + +2015-02-23 v1.8.14 + +- Fix a problem with u430 F9 key when "Use all F1, F2..." is selected. This resulted in a new way to send both make/break keys from ACPI Notify (0x03xx and 0x04xx). + + +2014-10-16 v1.8.13 + +- Default for USBMouseStopsTrackpad is now zero instead of one. This means the trackpad will not be disabled at the login screen when a USB mouse is plugged in. + +- turn off FakeMiddleButton in VoodooPS2Mouse.kext + +- some tweaks for ideapad + +- tuned movement/acceleration to better match the MacBookAir6,2. Changed resolution to 400 from higher values, which seems to help... + +- other fixes/changes: see commit log for more information + + +2014-05-23 v1.8.12 + +- Fix bugs. See commit log/issues database for details. + +- Finished Macro Inversion option for converting Fn+fkeys in challenging situations. + +- Lenovo u430 profile is working well now (due to Macro Inversion and other features). + + +2014-02-24 v1.8.11 + +- Implement ability to send key strokes from ACPI. This is useful when certain keys are not handled via the PS2 bus, but instead are handled elsewhere in DSDT, usually via EC query methods. With this I was able to make the native brightness keys work as normal keys on a Lenovo U430. + +- Implement ability to call into ACPI when keys are pressed. Scan codes e0f0 through e0ff are reserved to call back into RKAx (where X is a hex digit corresponding to the last digit of the scan code, eg. 0-9 A-F). Use Custom PS2 Map to map a real scan code to e0f0-e0ff, then define RKA0 up to RKAF in your PS2K device. Note: Your keyboard device must be called PS2K. + +- A few pull requests from others. See commit log. + +- [wip] Profile for Lenovo u430 keyboard. + + +2014-01-21 v1.8.10 + +- Implement BrightnessHack option for laptops with non-working Brightness keys. If set in the keyboard Info.plist, this setting will allow screen brightness to be changed via Ctrl+Alt+NumPad Plus/Minus keys. + + +2013-12-05 v1.8.9 + +- Fixed F1 sleep issue in Mavericks + +- Added Platform Profiles for various laptops (HP 6560b, Dell N5110), including special key switching for certain Dell/Samsung machines. + +- Added workaround for unexpected trackpad data + + +2013-09-13 v1.8.8 + +- Fixed jitter/twitching that happens when using two finger scroll on 10.9 Mavericks. Threshold is currently 10. Can be customized in the trackpad Info.plist with the ScrollDeltaThreshX and ScrollDeltaThreshY items. + +- Implemented "Dragging" (double-tap-hold for drag) to be more like a real Mac. Now the drag will remain in effect for one second after releasing your finger from the touchpad (before it was immediate). This makes it easier to drag/resize/extend selections larger distance. The timeout is controlled by DragExitDelayTime in the trackpad Info.plist. It can be changed to zero (0) to revert to the original behavior. + +- Implemented a slight tweak to the averaging algorithm. Now using 4 samples instead of 3. + + +2013-08-15 v1.8.7 + +- Fix a bug which prevents VoodooPS2Controller.kext from loading on 10.9 Mavericks. The problem was the class ApplePS2Controller was not exported properly. + +2013-08-05 v1.8.6 + +- Add support for HPQOEM:17F3 (HP ProBook 4440s) + +2013-07-01 v1.8.5 + +- Added support for HPQOEM:17F0 (HP ProBook 4340s) + +- Corrected error in setProperties for ApplePS2Controller. Now works with utility 'ioio' + + +2013-05-26 v1.8.4 + +- Added option to override DSDT oemId/oemTableId via ioreg properties on PS2K device. This is for clover as it is necessary to override oemId because Clover patches the DSDT to reflect oemId as "Apple ". In order to work around this bug, we can now provide an "RM,oem-id" property in the PS2K device. Although it isn't necessary (for Clover), you can also provide an override oemTableId via a property "RM,oem-table-id". For an example of use, see the ProBook 4530s patches (02_DSDTPatch.txt, search for PS2K). This allows you to use the driver unmodified on Clover and still get the proper configuration selected via Platform Profile setup, provided you have the proper injection in your DSDT. This may end up being useful for other computers as well, when the OEM has chosen poor names for oemId/oemTableId. + + +2013-05-07 v1.8.3 + +- WakeDelay in ioreg for ApplePS2Controller is now correct integer type instead of bool. + +- Added support for HP ProBook 5330m. + +- Eliminated FakeSMC dependency. The drivers now look directly at DSDT in ioreg to determine what to use for configuration instead of the "mb-product"/"mb-manufacturer" properties of FakeSMC. + +- Remove extraneous mapping for R_OPTION in ProBook-87 keyboard configuration. + + +2013-04-07 v1.8.2 + +- Fixed problem under Snow Leopard where VoodooPS2Keyboard.kext was not loading. + +- Fix issue with Eject (Insert) causes DVD to eject immediately instead of after a delay. Now the driver honors the HIDF12EjectDelay configuration setting, which by default is 250 ms. You can also change it in the Info.plist if you wish. 250 ms provides a slight delay that avoids accidental eject. + +- Added support for HP:4111 keyboard map/configuration for HP ProBook 4520s. + +- Changed the default assignment of the right "menu/application" key (on 4530s in between right Alt and right Ctrl). Prior to this release it was assigned to Apple 'Fn' key. Now it is assigned such that it is 'right Windows' which maps to Apple 'option.' This is configurable in the keyboard driver's Info.plist. + +- Fixed a bug where special functions/media keys were not mapped to F-keys by default until you checked then unchecked the option for this in SysPrefs -> Keyboard. + + +2013-03-15 v1.8.1 + +- New feature: It is now possible to toggle the Keyboard Prefs "Use all F1, F2…" option with a keyboard hotkey. On the ProBook 4xx0s series, it is Ctrl+'prt sc'. + +- New feature: Added keyboard mapping/ADB (92) code for the Eject key. On the ProBook 4xx0s series, it is assigned to the 'insert' key. See this link for more information on the various modifiers that can be used with the Eject key: http://support.apple.com/kb/ht1343 + +- Supported added to Info.plist for keyboards on ProBook 8460p/6470b laptops. Credits to nguyenmac and kpkp. + +- Platform Profile computer specific sections in Info.plist are now merged with the 'Default' section making it easier to create/manage alternate configurations, because you only have to enter the differences/overrides. + +- Fixed a bug in the ProBook specific Info.plist (for those not using kozlek's FakeSMC) where SleepPressTime was not set to 3000. + +- Developers: Updated to Xcode 4.61, and also optimized a bit for size and exporting less symbols into kernel namespace. + + +2013-03-04 v1.8.0 + +- Feature: Info.plist content is now driven off the mb-manufacturer/mb-product ioreg properties provided by kozlek's FakeSMC. This allows different keyboard layouts/trackpad settings to be based on which machine/motherboard the drivers find themselves running on. As of this time, support has been added for the Probook 4x30s series (HP/167C). Users who create custom settings for other hardware are encouraged to submit their Info.plist changes and mb-manufacturer/mb-product IDs from the FakeSMC device in the ioreg. I will integrate these new profiles into future builds. This also means that I'm only distributing one version of the package from now on, with special instructions for ProBook users not using the latest FakeSMC. + +- bug fix: Fixed UnsmoothInput option. It was not working at all. + +- Changed the default Resolution/ScrollResolution from 2300 to 2950. This results in more control for finer movements, but slower overall acceleration. You may have to adjust your pointer speeds in System Prefs -> Trackpad to suit your preference. + +- bug fix: Fixed a problem on Snow Leopard if Kernel Cache was being used. + +- For developers: Fixed 32-bit build. + + +2013-02-26 v1.7.17 + +- bug fix: Evidently there is an OS X bug in IOMallocAligned. For some reason only affects the debug version of this driver (Release version was fine for some unknown reason). Temporarily move back to IOMalloc until it can be resolved. + + +2013-02-25 v1.7.16 + +- bug fix: Fix some problems with pass through packets (pass through capability applies if you have both a trackpad and a stick). + +- Add support for 'genADB' for keyboards without a number pad. Hold down Alt (command), then type digits to simulate an ADB code, then release Alt. This features is only available with the DEBUG version. Previously this worked only with the numpad digits. + +- Add LogScanCodes property to keyboard driver. Now you can log scan codes to the Console system.log, even in the Release version. Use 'ioio' to set this property to true, and you will see scan codes logged in the Console. Set it back to false when you're done. + + +2013-02-21 v1.7.15a (beta) + +- bug fix: Fix problem (again) with startup sequence (a multithreaded issue), where it was causing the keyboard to be non-responsive or indefinitely repeat a key. + + +2013-02-20 v1.7.15 (beta) + +- Slight tweak to middle button handling: The code will now commit to a single left/right button if you touch the touchpad after pressing one of the buttons (instead of waiting the 100ms to see if a middle button event should happen instead). + +- bug fix: Fixed a problem with startup (multithreaded issue), where the keyboard would not work or indefinitely repeat at the login screen. + +- bug fix: Fix problem with holding down Alt and using numpad digits to type an ADB code (a feature only for debug mode). Had to undo most of "non-chaotic" startup and approach from another angle. + +- New feature: Some keyboards do not generate 'break' codes when a key is released. This causes OS X to believe the user is holding the key down. Fn+F1 through Fn+F12 can have this. Seems to be common on Dell laptops. Since I failed to find a way to force the keyboard to generate break codes for these keys (some controllers seem to just ignore this), I have implemented the ability to specify these keys by their scan code in the keyboard driver's Info.plist. You must specify the scan code of each key in the "Breakless PS2" section. See VoodooPS2Keyboard/VoodooPS2Keyboard-Breakless-Info.plist for an example (tested with my Probook keyboard for all Fn+fkeys). + + +2013-02-17 v1.7.14 (beta) + +- Bug fix: Fixed problem where Synaptics trackpad would load even if a Synaptics device was not detected. + +- Internal change: Re-wrote the interrupt handling code. Prior to this version, data was not pulled from the PS2 port until the "work loop" got around to it. All the interrupt routines did was signal the scheduler to wake the work loop. With this version, data is collected directly in the interrupt routine, placed in a ring buffer, and the work loop is only scheduled when a complete packet has arrived. So, for the keyboard driver, the work loop is activated whenever a full scan code sequence has been sent (from 1 to 3 bytes), for the mouse driver, when a full 3-byte packet has arrived, and for the trackpad driver, when a full 6-byte packet has arrived. Not only is this more efficient (only scheduling the work loop when there is actual work to do), it is also safer as data is gathered from the PS2 port as soon as it is available. This is a pretty major change. + +- setProperties/setParamProperties is now fully implemented in the keyboard driver. This means you can use 'ioio' to change configuration values on demand. It also means that eventually the prefpane will be able to manipulate the keyboard Info.plist options just like it can with the trackpad. When I get around to working on the prefpane… + +- Added an option to most Info.plist to allow a device to be "disabled." By setting DisableDevice to true, you can keep that driver code from loading and being considered for probing. By default, none of the drivers are disabled, but disabling them may improve startup performance as well as reduce the chances of things going wrong. I think a future version of the ProBook Installer should automatically disable the devices not used on the ProBook. + +- Implemented a "non-chaotic" startup. Turns out that OS X will start all devices in parallel. While this can make the system start up faster, it might not be such a good idea for devices interacting with one resource, in this case, the PS2 port. The keyboard driver now waits for the PS2Controller to finish starting before starting it's "probe" process. And the mouse/trackpad drivers wait for the keyboard driver to finish starting before starting their "probe" process. A future version may make this optional. + +- The keyboard driver's "probe" function now will always return success. I have not found a reliable way to detect a keyboard actually being present or not, so it will always load and in the Release version does not test for the keyboard existing at all. Also important to note that the mouse/trackpad drivers now wait for the keyboard to load (see above), so it is really necessary to have the keyboard driver loading always anyway. + +- Added a special case for Synaptics type 0x46 (really a hack for Probooks with a Synaptics stick) to report led present, because we know it is there and it works. + +- Cleaned up some of the logic for middle button clicks, such that releasing either button will immediately release the middle button. + + +Note: v1.7.13 skipped. + + +2013-02-07 v1.7.12 (beta) + +- Implemented middle click by clicking both the left and right physical buttons at one time. Time for button clicks to be considered middle instead of right/left is configurable as MiddleClickTime. Default is 100ms. + +- Implemented a new option in the Info.plist for the trackpad: ImmediateClick. Set by default to false. If true, it changes the behavior of clicking and dragging with tap and double-tap hold. Prior to this option, a tap does not register its button up until the double click time has passed. For some actions and clicking on some buttons (when the application does the button action after the mouse button up has been received), this made the touchpad clicks seem sluggish. The reason this was necessary was to make the double-tap-hold for drag to work… to make it work, the button is held for the duration of the double click time, thus the delay for the button up. If this was not done, certain drag operations wouldn't work because of the way the system is written. Some drag operations do not start on a double-click. For example, dragging a window will not start if it is on a double click. You can try this with a mouse (double-click-hold, then try to drag -- it doesn't take). That's why the original code holds that button even though you've already completed the click with the first tap: it had to otherwise the next tap as part of the double-tap-hold sequence would be seen as a double-click and dragging the title bar wouldn't work. OS X is very inconsistent here with this. For example, you can double-click-drag a scroll bar. When ImmediateClick is set to true, after you complete the tap the button will immediately be reported as up (it is only down for a short time). In order to make double-tap-hold still work for dragging windows on the desktop, the button down for the second tap (tap+hold really) is not sent until at least double click time has passed. This means that dragging does not engage for a little bit after the double-tap-hold sequence is initiated. + +- Internal: General cleanup, especially around manipulation of the command byte. + + +2013-02-04 v1.7.11 (beta) + +- Fixed a bug, previously documented as a known issue, where some trackpads were unresponsive after waking up from sleep (Probook 4540s, for example). The fix is to re-initialize the 8042 keyboard controller on wake from sleep and to initialize the keyboard first, mouse second after wake from sleep instead of the original opposite order. + +- Fixed a bug, previously documented as a known issue, where if your trackpad was in absolute mode (using VoodooPS2Trackpad.kext) and you restarted without turning off the laptop after switching to using only the mouse driver (VoodooPS2Mouse.kext), the trackpad was not correctly reset into relative mode and as such it didn't work properly. The same thing would happen on transitions from other operating systems (Windows or Ubuntu) and then booting into OS X using VoodooPS2Mouse.kext. + +- Rarely, the keyboard and trackpad would stop working, especially just after logging in. Since this is an intermittent problem, it is difficult to tell if this is fixed. But it seemed to be getting worse lately. And there is a lot more properties being set from the system in setParamProperties (because the drivers are responding to more and more settings available in System Preferences). These property settings happen at login… to apply the user's preferences. After looking at some sources for IOHIDSystem, I discovered Apple routes all work for setParamProperties through a command gate in order to synchronize on the work-loop thread. This fix is now implemented. + +- VoodooPS2Mouse.kext now will now send Synaptics specific data only when it is detected a Synaptics device. This was an issue specifically with ActLikeTrackpad option in VoodooPS2Mouse.kext. + + +2013-01-29 v1.7.10 (beta) + +- Fixed bugs in ClickPad support. Especially right click logic. + +- Time from first touch to clicking "pad button" is now configurable for ClickPads. Info.plist variable is ClickPadClickTime (Default is 300ms) + +- It is possible again to build a 32-bit version, should it be needed. I am still not providing 32-bit capability with the official builds. + + +2013-01-27 v1.7.9 (beta) +- Added capability to scale interleaved PS/2 "passthrough" packets to scale the resolution up to the trackpad resolution. See MouseMultiplierX and MouseMultiplierY in the trackpad's Info.plist + +- Modifier key(s) used for "temporary drag lock" feature is now configurable (previous release it was hardcoded to control). This is controlled by DragLockTempMask in the trackpad Info.plist. Set to 262148 for control key, 524296 for command (alt) key, and 1048592 for option (windows) key. Please note the default configuration of the keyboard Info.plist has the command and option swapped, so in that case, it is 1048592 for option (windows) key, and 524296 for the command (alt) key. + +- Swipe Up, Down, Left, Right are now assigned by default to the following keyboard combinations: Control+Command+UpArrow, Control+Command+DownArrow, Control+Command+LeftArrow, Control+Command+RightArrow. This should work better with international keyboards. You will need to use System Preferences -> Keyboard -> Keyboard Shortcuts to adjust to assign these keys to your desired actions. If you were using three finger swipe left and right for back/forward in your web browser, you will need to reconfigure these actions via the Info.plist or use a program like KeyRemap4MacBook to remap the keys generated to the keys used by your browser for forward/back (that's what I plan to do). + +- Implemented support for System Preferences -> Keyboard -> "Use All F1, F2, etc. keys as standard function keys." Now it is possible to have the Fn+fkeys/fkeys swap honor this setting in System Preferences. But to enable this feature, the Info.plist must contain two new items "Function Keys Standard" and "Function Keys Special" If these items are present, then the option will be available in System Preferences -> Keyboard. If not, the option is not available. The format of these two keys is the same as "Custom PS2 Map" the difference being that "Function Keys Standard" is in effect when the option is checked, and "Function Keys Special" is invoked when the option is not checked. The proper mapping is implemented for the Probook 4x30s in VoodooPS2Keyboard-RemapFN-Info.plist. In "Function Keys Standard" the mapping is removed. And in "Function Keys Special" fn+fkeys and fkeys are swapped. Any laptop should be able to have support created for it by modifying these keys as long as the scan codes can be determined (Fn+fkeys scan codes vary between specific laptop models). + +- Cleaned up keyboard debug messages to make it easier to create custom key mappings. Eventually, the wiki on keyboard remapping will reflect this. + +- Implemented support for changing the keyboard backlight on certain notebooks. See this thread for further information: http://www.tonymacx86.com/mountain-lion-laptop-support/86141-asus-g73jh-keyboard-backlighting-working.html + +- Implemented support for changing screen brightness via ACPI methods in the DSDT. You need some understanding of ACPI to try this feature. + + +2013-01-24 v1.7.8 (beta) +- Added acceleration table as suggested by valko. This makes small movements more precise and large movements quicker. + +- Implemented "Momentum Scroll." This allows scrolling to continue after you have released from the trackpad. There is probably some work that could still be done here to make it match the feel of a real Mac, but I think it may be close. Please provide feedback. This feature is enabled by default, but you can turn it off in System Preferences -> Accessibility -> Mouse & Trackpad -> Trackpad Options. + +- Implemented support for System Preferences -> Accessibility -> "Ignore built-in trackpad when mouse or wireless trackpad is present" If set, the trackpad will be disabled when there is one or more USB mice plugged in. You must install the VoodooPS2Daemon as described in the installation instructions for this to work. This is also implemented for VoodooPS2Mouse.kext if ActLikeTrackpad is set. + +- Added a "temporary Drag Lock" feature. If you enter Drag (double tap+hold) with the Command key down, it will be as if you had "Drag Lock" set in trackpad preferences, but just for that drag operation. The drag is ended by tapping, just like normal drag lock. + +- Added support for "middle button." You can get a middle button click if you use three-finger tap. This is enabled by setting ButtonCount to 3 in Info.plist. If this causes an issue or you wish to disable it, set ButtonCount to 2 in the Info.plist. In addition, if you wish to reverse the function of two-finger tap and three-finger tap, set SwapDoubleTriple to true in the Info.plist. + +- Added support for Synaptics ClickPad(â„¢). These trackpads have a single button under the entire pad. In order to make these trackpads usable, the trackpad must be placed into "Extended W Mode" which allows the driver to obtain data from both a primary and secondary finger. Support for these trackpads should be considered experimental since it has only been tested via simulation with a Probook trackpad (which is not a ClickPad). Let me know how/if it works. + +- Key sequences for trackpad 3-finger swipes are now configurable in the keyboard driver Info.plist. Any combination of keys can be sent. Controlled by the following configuration items: ActionSwipeUp, ActionSwipeDown, ActionSwipeLeft, ActionSwipeRight. + +- By default, the horizontal scroll area at the bottom and the vertical scroll area at the right are not enabled. You can re-enable them by setting the HorizonalScrollDivisor and VerticalScrollDivisor to one (1). + +- Fixed a bug where if the trackpad was "disabled" before a system restart, the LED remained lit. The LED is now turned off at boot and during shutdown/restart. + +- Separated Divisor in Info.plist to DivisorX and DivisorY. This may help those of you with different trackpads than the Probook one. For the Probook both of these variables are set to one (no adjustment). + +- Started tweaking synapticsconfigload and the Preference Pane. These features are not ready for general use yet, and therefore are not included in the binary distribution. + +- For developers: Added makefile for command line builds, and added shared schemes to project to make it easier to build. + + +2012-11-29 v1.7.7 +- Integrated the chiby/valko trackpad init code for non-HP Probook trackpads. This is for wake from sleep and cold boot initialize using undocumented trackpad commands determined by chiby by monitoring the communication line between the PC and the trackpad. This should allow the trackpad driver to work with more models of trackpads. + +- Integrated valko three-finger gesture code (with tweaks). These 3-finger gestures are as follows: + swipe left: sends keyboard Command+[ + swipe right: sends keyboard Command+] + swipe up: sends keyboard F9 + swipe down: sends keyboard F10 + +A future version will allow these command mappings to be changed in the Info.plist. In this version, they are hard-coded. + +The amount of movement is controlled by SwipeDeltaX and SwipeDeltaY in the Info.plist. The default for this version is 800. + +So, Command+[, Command+] should correspond loosely to back/forward, respectively. And F9 and F10 can be mapped to various functions (Launchpad, Show Desktop, Mission Control, etc.) by changing the assignments in the System Preferences -> Keyboard. + +- Changed the 2-finger scroll logic to allow two-fingers held very tightly, even with "ignore accidental trackpad input" turned on. The trackpad driver sends bad data when the two fingers are held together like this (it sends it as one 'wide' finger). + + +2012-10-30 v1.7.6 +- Changed the default value of MaxDragTime in Info.plist for trackpad. Anything larger than 240ms will cause incorrect behavior with the Finder's forward and back buttons. Changed it to 230ms to avoid this issue. + + +2012-10-20 v1.7.5 +- Added default behaviors for Fn+F4, Fn+F5, Fn+F6. Fn+F4 is "Video Mirror" -- it toggles display mirror mode. Fn+F5 is Mission Control. Fn+F6 is Launchpad. These keys were previously unmapped by default (when no Custom ADB Map was present in Info.plist). + +- In the debug version only, added the ability to generate any ADB code you want. To do so, hold down Alt, then type the ADB code with the numpad number keys (0-9). The resulting code is sent after you release the Alt key. This was how I discovered the ADB code for the Launchpad is 0x83 (Alt-down, 1, 3, 1, Alt-up). + +- "Just for fun"... implemented three finger salute. + +- Fixed a bug where key repeat for keys with extended scan codes (those that start with e0) may not have been repeating properly. This bug was introduced when the keyboard mapping feature was added. + +- Made scrolling (both multi-finger and single-finger) much, much smoother. + +- Allow transitions from horizontal scrolling to vertical scrolling without falling into "move" mode. This allows you to horizontally scroll right across the bottom of the pad, and onto the bezel, then returning back onto the pad (without lifting your finger) to resume horizontal scrolling. Although not very useful, you can also horizontally scroll into the lower left corner, then move up to vertically scroll in the right margin. The previous version would "lose" the scroll mode when moving off the right side or the horizontal scroll zone (because upon reentry, it would enter vertical scroll mode and not be able to resume horizontal scroll mode upon entering the horizontal scroll margin area). + +- Fixed a bug where trackpad input/pointer position would demonstrate a slight glitch when changing the trackpad configuration in System Preferences. + +- Added ability to disable/enable trackpad by double tapping on the upper left corner. The area of the trackpad that is used for this function is configurable in the Info.plist. By default (DisableZoneControl=0) this feature is enabled if your trackpad has an LED. You can disable this feature by setting DisableZoneControl=-1 in Info.plist. You can enable this feature for trackpads that don't have an LED by setting DisableZoneControl=1. + +- Added a smoothing algorithm to process the input from the trackpad. Still experimenting with this to tweak the parameters, but it is coming along. This is controlled by two Info.plist settings: SmoothInput and UnsmoothInput. By default, the trackpad itself does a little smoothing on its own (1:2 decaying average). If you set the UnsmoothInput option, it will undo the action the trackpad is implementing (a decaying average can be mathematically reversed). If you set SmoothInput, a simple average with a history of three samples is used. By default, both UnsmoothInput and SmoothInput are set. + +- Added a movement threshold for tap to left click and two-finger tap to right click. For left clicks the threshold is 50. So if while tapping, you move more than 50, the tap is not converted to a click. The threshold for right clicks is 100 as there tends to be more movement detected from the trackpad hardware with a two finger tap. These values can be adjusted in Info.plist. This was mainly put in place to avoid accidental entry into drag mode when rapidly moving (with multiple quick swipes across the trackpad). + +- Palm rejection/accidental input now honors system trackpad preferences setting "Ignore Accidental Trackpad Input", so you can turn it off. I would not recommend turning it off. The system actually sets three different options when you enable this option in System Preferences ("PalmNoAction While Typing", "PalmNoAction Permanent", and "OutsidezoneNoAction When Typing"). The Trackpad code pays attention to each one separately, although they are all set or cleared as a group. Perhaps there is some command line way of setting them individually. + +- Implements a defined zone in the left and right margins (and potentially top and bottom) where input is ignored while typing (see Zone* in Info.plist). This is enabled if you "Ignore Accidental Trackpad Input" + +- Modifier keys going down are ignored as far as determining timing related to accidental input. This allows you to position the pointer with the trackpad over something you want to click on (say a website URL) and then hold Ctrl (or other modifier) then tap to click. This is only for keydown events and only for shift, control, alt(command), and windows(option) keys. + +- Trackpad code now determines automatically if your Trackpad has an LED and disables turning on/off the LED if it isn't present. + +- Trackpad code now determines automatically if your Trackpad has pass through support and enables pass through only if the guest PS/2 device is present. This avoids bad things happening (mouse buttons getting stuck down) if a non-pass through trackpad sends pass through packets. + +- The Mouse driver in this version has minimal support for "Ignore Accidental Trackpad Input". In particular, it is able to ignore buttons and scrolling while typing. Note that this means real buttons too, not just simulated buttons via tapping (since it is a mouse driver, it can't tell the difference). This feature is only in effect if "ActLikeTrackpad" is set to Yes in Info.plist. + +- You can make the Mouse driver act like a trackpad. If you set "ActLikeTrackpad" to Yes in the Info.plist, the mouse driver will enable trackpad like features. This includes the Trackpad settings in System Preferences (although many options don't have an effect). This allows you to turn on/off scrolling, as well as "Ignore Accidental Trackpad Input" + +- There is also support for enabling and disabling the mouse (trackpad) with the keyboard, including support for the Synaptics LED. You probably only want to set this if you actually have a Synaptics, as the code doesn't quite check properly. + + +2012-10-15 v1.7.4 +- Implemented experimental support for pass through packets from a guest PS2 device. These are 3-byte packets encapsulated in a 6-byte touchpad packet that are used to interleave input from a guest device such as a track point stick. I don't have such a device, but I've implemented code to pass through the x & y deltas, as well as some logic to deal with merging the buttons from that device and the trackpad. + +- Improved handling of two-finger tap for right-click. There is still more to come in this area + +- Improved ignoring of accidental tap to click, two-finger tap to click. + +- Improved clicking on system menu when double click time is relatively long. + +- Changed keyboard map to include functions for F4 and F5. F4 is video mirror toggle. F5 is dashboard. + +- Integrated AppleACPIPS2Nub.kext into VoodooPS2Controller.kext. +(IMPORTANT: This means AppleACPIPS2Nub.kext is not required and MUST be removed for a properly working system). + + +2012-10-13 v1.7.3 +- OOPS bug: Now the sleep button timeout really works. + +- Bug fix: VoodooPS2Mouse.kext now works… At least it does with the HP Touchpad. There was a problem with the way the mouse was being initialized. Hopefully that doesn't break regular PS/2 mice. This feature might be useful for people who can't use the touchpad driver for one reason or another. In the near future, I plan to implement some of the "ignore accidental input" features into this driver. + + +2012-10-13 v1.7.2 +- Add capability to make custom keyboard maps. Both ps2 to ps2 scan code mapping and ps2 to adb mapping is available by creating a simple table in Info.plist. Eventually, I'll write a wiki explaining how custom keyboard maps work. + +- Implement option in Info.plist to control how the sleep button works. By setting SleepPressTime (expressed in milliseconds) to something other than zero, you can control how long the user must press the sleep button before the laptop enters sleep. Proper function here depends on the scan code being an auto-repeat scan code. So, if you assign a sleep time with the normal scan code table, you will have to press Fn+F1 for the time, then release (because it doesn't repeat). This is primarily for use in conjunction with remapping the keyboard. If you wanted to swap the Fn+Fkeys with Fkeys, for example, your sleep button would become F1. Since it is very easy to hit F1 by accident (while reaching for Esc), you can use this option to keep from invoking the sleep function accidentally. + +- Default layout of keys uses keyboard mapping to swap Fkeys for Fn+fkeys. + +- Fixed the bug where if you had dragging enabled, and you tapped on a menu, the menu would go away most of the time. + +- Improved detection of accidental input especially while typing. Note: This is extremely new code and probably is not perfect. Please report any problems you see. + +- Fixed a bug where turning on/off trackpad caused a bonk sound. + +- Added support for Synaptics trackpad type 0x46 (normal is 0x47). In this case, the LED setting will be disabled for wake from sleep. But there is still more work to support this trackpad to come as time/information permits. + + +2012-10-11 v1.7.1: +- Fix problem with WiFi key (again). I had it fixed once, but evidently regressed at some point. + + +2012-10-10 v1.7.0: +- Basics of palm rejection are implemented. Still need to make the code pay attention to system settings + +- Fn+Del(sysreq) is implemented as a way to toggle touchpad on/off + (on 4x30s keyboards without numpad, it seems to be Fn+Insert(prtscrn)) + (this would include the 4230, 4330, and 4430) + + +2012-10-04 +- Initial copy/commit. + + +### History + +This repository contains a modified VoodooPS2Controller. Original sources came from this post on Insanely Mac: + +http://www.insanelymac.com/forum/topic/236835-updated-2012-genericbrightnesskext/ + +The sources were pretty old at the time I pulled them (last modified 2009), so there were a few bug fixes they were missing (and could still be missing), but at this point it is the best source base I've found. Current Voodoo sources, in my opinion, were not workable. I couldn't make the Touchpad work at all, and the keyboard driver was doing very strange things (brightness causing computer to reboot). They just didn't seem very close to the versions that we have been using for the HP ProBook. Certainly it would be possible to reconcile these two versions, but after comparing one to the other, I couldn't see any reason to. + +I have tried to make the initial commit of this code reasonably close to the sources which I downloaded from the link mentioned above. That way it should be easy to see the progression forward. The commits after that are a different story. There is the occasional gratuitous change that I made while reviewing and attempting to understand the code. That's what you get from a guy that is working for free. + +Please note that I'm only testing this on my HP ProBook 4530s. Although it should work fine on other laptops with a Synaptics Trackpad, I haven't tested it on anything but my computer. By the way, the HP ProBook's trackpad reports version 7.5. Related to this is the fact that I haven't tested the "mouse only" part of this driver (ie. VoodooPS2Mouse.kext). I don't have a desktop hack at this point, and even if I did, I don't have any desktop computers with PS/2 ports. + +Also, there is a Preference Pane in here, and perhaps at one time it worked. I haven't tested it. Not at all. It builds and that is all I know. At some point, I will take a look at that, and perhaps add some features or at least make it work. + +Documentation on the Synaptics hardware can be obtained (at least at the time I write this) from the Synaptics website: + +http://www.synaptics.com/resources/developers + +I based my code here on the "Synaptics PS/2 TouchPad Interfacing Guide, PN 511-000275-01 Rev.B" I would include the document in the github repository here, but the document is copyrighted and I didn't want to ruffle any feathers. + + +### Future + +My intention is to eventually enhance both the Synaptics Trackpad support as well as they keyboard to support the following features: + + +Touchpad: + +- disable touchpad if USB mouse is plugged in and "Ignore built-in trackpad when mouse or wireless trackpad is present" in Accessibility settings in System Preferences. + (DONE) + +- calibrate movement/sensitivity to mouse + (since they share the same config, it would be great not to have to adjust) + (note: they are pretty close, but could be tweaked a bit) + (DONE) + +- investigate using extended-W mode + (haven't done much here except read the spec) + +- more gestures, as time permits (currently two-finger scrolling and three-finger swipe) + +- implement touch pad on/off in upper left corner + (DONE) + +- clean up IOLog and allow for more information in Debug version + (DONE) + +- if possible, implement LED indication for touchpad on/off + (HP ProBook specific) + (DONE) + +- implement high resolution mode for Synaptics + (may already be implemented but not enabled in Info.plist) + (DONE -- this version seems smoother than the one we were using) + +- implement palm rejection (accidental input) + (DONE) + +- investigate doing something to make movement smoother + (implement some kind of decaying average to smooth spikes in the input stream) + (DONE) + +- implement a threshold of movement that will cancel a click drag + (this would avoid unwanted drag detection) + (one way to avoid this is for the user to set the fastest double click speed) + (DONE) + +- Fix bug where trying to open a Menu with a tap does not work: Menu opens, but + most of the time immediately closes. + (DONE) + + +Keyboard: + +- Correct volume indications + (for some reason these are not working right now) + (DONE) + +- Make wireless key work for turning wireless on/off + (HP ProBook specific) + (DONE) + +- Allow for some limited form of custom key mappings + (instead of hardcoding scan codes for specific laptops) + (DONE) + +- Allow Fn+Fkeys to be swapped for FKeys (without Fn) + (DONE -- use generic keyboard remapping above) + + +Mouse: + +- Implement LED on/off for Synaptics touch pads operating as a PS2 mouse + (DONE) + +- Make the VoodooPS2Mouse.kext work for trackpads in mouse simulation mode. For some reason it arrived broken when I got the code. + (DONE). + +- Add "ignore input after typing" features to mouse driver. A little weird to make for a real PS2 mouse, but super nice for laptops with trackpads operating in mouse simulation mode. + (DONE) + + + +PrefPane: + +- Maybe test it and see if it works (it works, but there is a lot of options that don't make sense for Probook users) + +- Also, it would be nice if preferences would stick across reboots... (this works via synapticsconfigload, but needs work) + + +### Original Credits + +VoodooPS2Controller (core): turbo + +Resolution fix for PS2Mouse: mackerintel + +Synaptics driver: mackerintel + +Sentelic Driver: nhand42 + +Alps driver: phb + +Keyboard fixes: Chunnan & jape + +Synaptics Prefpane design: bumby + +Synpatics Prefpane: mackerintel + +Great thanks to Dense for helping with activating vanilla trackpad prefpane diff --git a/VoodooPS2Controller.xcodeproj/project.pbxproj b/VoodooPS2Controller.xcodeproj/project.pbxproj new file mode 100644 index 0000000..b6a4853 --- /dev/null +++ b/VoodooPS2Controller.xcodeproj/project.pbxproj @@ -0,0 +1,997 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 48; + objects = { + +/* Begin PBXBuildFile section */ + 840F104A16EFE42600E8C116 /* ApplePS2Device.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 840F104916EFE42600E8C116 /* ApplePS2Device.cpp */; }; + 84167820161B55B2002C60E6 /* VoodooPS2Controller.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8416781F161B55B2002C60E6 /* VoodooPS2Controller.cpp */; }; + 84167836161B5613002C60E6 /* VoodooPS2Keyboard.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 84167835161B5613002C60E6 /* VoodooPS2Keyboard.cpp */; }; + 841FEF7E16539DDF00A4D4C8 /* IOKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 84833FCC161BA27700845294 /* IOKit.framework */; }; + 84833FA3161B627D00845294 /* ApplePS2Device.h in Headers */ = {isa = PBXBuildFile; fileRef = 84833F9D161B627D00845294 /* ApplePS2Device.h */; settings = {ATTRIBUTES = (); }; }; + 84833FA5161B627D00845294 /* ApplePS2KeyboardDevice.h in Headers */ = {isa = PBXBuildFile; fileRef = 84833F9F161B627D00845294 /* ApplePS2KeyboardDevice.h */; settings = {ATTRIBUTES = (); }; }; + 84833FA7161B627D00845294 /* ApplePS2MouseDevice.h in Headers */ = {isa = PBXBuildFile; fileRef = 84833FA1161B627D00845294 /* ApplePS2MouseDevice.h */; settings = {ATTRIBUTES = (); }; }; + 84833FAA161B629500845294 /* ApplePS2ToADBMap.h in Headers */ = {isa = PBXBuildFile; fileRef = 84833FA9161B629500845294 /* ApplePS2ToADBMap.h */; settings = {ATTRIBUTES = (); }; }; + 84833FB1161B62A900845294 /* alps.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 84833FAB161B62A900845294 /* alps.cpp */; }; + 84833FB2161B62A900845294 /* alps.h in Headers */ = {isa = PBXBuildFile; fileRef = 84833FAC161B62A900845294 /* alps.h */; settings = {ATTRIBUTES = (); }; }; + 84833FC2161B69C700845294 /* VoodooPS2Keyboard.h in Headers */ = {isa = PBXBuildFile; fileRef = 84167834161B5613002C60E6 /* VoodooPS2Keyboard.h */; settings = {ATTRIBUTES = (); }; }; + 84833FC3161B6A7E00845294 /* VoodooPS2Controller.h in Headers */ = {isa = PBXBuildFile; fileRef = 8416781E161B55B2002C60E6 /* VoodooPS2Controller.h */; settings = {ATTRIBUTES = (); }; }; + 84833FCD161BA27800845294 /* IOKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 84833FCC161BA27700845294 /* IOKit.framework */; }; + 84C337AA1698BC38009B8177 /* CoreFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 84C337A91698BC38009B8177 /* CoreFoundation.framework */; }; + 84C337AB1698BC5C009B8177 /* IOKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 84833FCC161BA27700845294 /* IOKit.framework */; }; + 84DD197B162D496E0044D061 /* AppleACPIPS2Nub.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 84DD1979162D496E0044D061 /* AppleACPIPS2Nub.cpp */; }; + 84DD197C162D496E0044D061 /* AppleACPIPS2Nub.h in Headers */ = {isa = PBXBuildFile; fileRef = 84DD197A162D496E0044D061 /* AppleACPIPS2Nub.h */; }; + 84EB0AE316F0AD9300016108 /* ApplePS2KeyboardDevice.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 84833F9E161B627D00845294 /* ApplePS2KeyboardDevice.cpp */; }; + 84EB0AE516F0AD9600016108 /* ApplePS2MouseDevice.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 84833FA0161B627D00845294 /* ApplePS2MouseDevice.cpp */; }; + 84F424E3161B59E500777765 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 84F424C3161B593D00777765 /* Cocoa.framework */; }; + 84F424E4161B59E500777765 /* PreferencePanes.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 84F424C5161B593D00777765 /* PreferencePanes.framework */; }; + BA560D361734DFF100914439 /* Decay.h in Headers */ = {isa = PBXBuildFile; fileRef = BA560D351734DFF100914439 /* Decay.h */; }; + BA5C70CF17338E7000E30E1A /* VoodooPS2TouchPadBase.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C3F4F41B76902F9877062D93 /* VoodooPS2TouchPadBase.cpp */; }; + BA5C70D017338E8600E30E1A /* VoodooPS2TouchPadBase.h in Headers */ = {isa = PBXBuildFile; fileRef = C3F4F859C067FD563476F515 /* VoodooPS2TouchPadBase.h */; }; +/* End PBXBuildFile section */ + +/* Begin PBXFileReference section */ + 8404565C161E3AAF00D74D7F /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; + 84045662161E3AD800D74D7F /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; + 84045665161E3AF900D74D7F /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; + 840F104916EFE42600E8C116 /* ApplePS2Device.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ApplePS2Device.cpp; sourceTree = ""; }; + 84167817161B55B2002C60E6 /* Kernel.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Kernel.framework; path = System/Library/Frameworks/Kernel.framework; sourceTree = SDKROOT; }; + 8416781A161B55B2002C60E6 /* VoodooPS2Controller-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "VoodooPS2Controller-Info.plist"; sourceTree = ""; }; + 8416781E161B55B2002C60E6 /* VoodooPS2Controller.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; lineEnding = 0; path = VoodooPS2Controller.h; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.objcpp; }; + 8416781F161B55B2002C60E6 /* VoodooPS2Controller.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; lineEnding = 0; path = VoodooPS2Controller.cpp; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.cpp; }; + 84167821161B55B2002C60E6 /* VoodooPS2Controller-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "VoodooPS2Controller-Prefix.pch"; sourceTree = ""; }; + 84167830161B5613002C60E6 /* VoodooPS2Keyboard-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "VoodooPS2Keyboard-Info.plist"; sourceTree = ""; }; + 84167834161B5613002C60E6 /* VoodooPS2Keyboard.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = VoodooPS2Keyboard.h; sourceTree = ""; }; + 84167835161B5613002C60E6 /* VoodooPS2Keyboard.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; lineEnding = 0; path = VoodooPS2Keyboard.cpp; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.cpp; }; + 84167837161B5613002C60E6 /* VoodooPS2Keyboard-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "VoodooPS2Keyboard-Prefix.pch"; sourceTree = ""; }; + 84167858161B56C4002C60E6 /* VoodooPS2Trackpad-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "VoodooPS2Trackpad-Info.plist"; sourceTree = ""; }; + 8416785F161B56C4002C60E6 /* VoodooPS2Trackpad-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "VoodooPS2Trackpad-Prefix.pch"; sourceTree = ""; }; + 8437048016284F66005B3C76 /* VoodooPS2Keyboard-RemapFN-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "VoodooPS2Keyboard-RemapFN-Info.plist"; sourceTree = ""; }; + 8441070016D4F68A0063F063 /* VoodooPS2Keyboard-Breakless-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "VoodooPS2Keyboard-Breakless-Info.plist"; sourceTree = ""; }; + 844952F1169A2696003DA49F /* makefile */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.make; path = makefile; sourceTree = ""; usesTabs = 1; }; + 84833F9D161B627D00845294 /* ApplePS2Device.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ApplePS2Device.h; path = VoodooPS2Controller/ApplePS2Device.h; sourceTree = ""; }; + 84833F9E161B627D00845294 /* ApplePS2KeyboardDevice.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ApplePS2KeyboardDevice.cpp; sourceTree = ""; }; + 84833F9F161B627D00845294 /* ApplePS2KeyboardDevice.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ApplePS2KeyboardDevice.h; path = VoodooPS2Controller/ApplePS2KeyboardDevice.h; sourceTree = ""; }; + 84833FA0161B627D00845294 /* ApplePS2MouseDevice.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ApplePS2MouseDevice.cpp; sourceTree = ""; }; + 84833FA1161B627D00845294 /* ApplePS2MouseDevice.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ApplePS2MouseDevice.h; path = VoodooPS2Controller/ApplePS2MouseDevice.h; sourceTree = ""; }; + 84833FA9161B629500845294 /* ApplePS2ToADBMap.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ApplePS2ToADBMap.h; sourceTree = ""; }; + 84833FAB161B62A900845294 /* alps.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; lineEnding = 0; path = alps.cpp; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.cpp; }; + 84833FAC161B62A900845294 /* alps.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = alps.h; sourceTree = ""; }; + 84833FCC161BA27700845294 /* IOKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = IOKit.framework; path = System/Library/Frameworks/IOKit.framework; sourceTree = SDKROOT; }; + 84C337A91698BC38009B8177 /* CoreFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreFoundation.framework; path = System/Library/Frameworks/CoreFoundation.framework; sourceTree = SDKROOT; }; + 84DD1979162D496E0044D061 /* AppleACPIPS2Nub.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = AppleACPIPS2Nub.cpp; sourceTree = ""; }; + 84DD197A162D496E0044D061 /* AppleACPIPS2Nub.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppleACPIPS2Nub.h; sourceTree = ""; }; + 84E9BAC816BE4C1300EEEB63 /* new_kext.h */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.h; path = new_kext.h; sourceTree = ""; }; + 84E9BACA16BE4CB300EEEB63 /* new_kext.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = new_kext.cpp; sourceTree = ""; }; + 84F424C3161B593D00777765 /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = System/Library/Frameworks/Cocoa.framework; sourceTree = SDKROOT; }; + 84F424C5161B593D00777765 /* PreferencePanes.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = PreferencePanes.framework; path = System/Library/Frameworks/PreferencePanes.framework; sourceTree = SDKROOT; }; + 84F424C7161B593D00777765 /* AppKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppKit.framework; path = System/Library/Frameworks/AppKit.framework; sourceTree = SDKROOT; }; + 84F424C8161B593D00777765 /* CoreData.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreData.framework; path = System/Library/Frameworks/CoreData.framework; sourceTree = SDKROOT; }; + 84F424C9161B593D00777765 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; + BA560D351734DFF100914439 /* Decay.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Decay.h; sourceTree = ""; }; + C3F4F41B76902F9877062D93 /* VoodooPS2TouchPadBase.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = VoodooPS2TouchPadBase.cpp; sourceTree = ""; }; + C3F4F603A234F724795A2FBD /* VoodooPS2Trackpad.kext */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.kernel-extension"; name = VoodooPS2Trackpad.kext; path = "../../Library/Caches/appCode20/DerivedData/VoodooPS2Controller-5fc0befb/Build/Products/Debug/VoodooPS2Trackpad.kext"; sourceTree = ""; }; + C3F4F859C067FD563476F515 /* VoodooPS2TouchPadBase.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = VoodooPS2TouchPadBase.h; sourceTree = ""; }; + C3F4FA022A4265DD37A85F4D /* VoodooPS2Controller.kext */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.kernel-extension"; name = VoodooPS2Controller.kext; path = "../../Library/Caches/appCode20/DerivedData/VoodooPS2Controller-5fc0befb/Build/Products/Debug/VoodooPS2Controller.kext"; sourceTree = ""; }; + C3F4FAC34E9069A684BFE9E1 /* VoodooPS2Keyboard.kext */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.kernel-extension"; name = VoodooPS2Keyboard.kext; path = "../../Library/Caches/appCode20/DerivedData/VoodooPS2Controller-5fc0befb/Build/Products/Debug/VoodooPS2Keyboard.kext"; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 8416780E161B55B2002C60E6 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 84167828161B5613002C60E6 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 8416783C161B56A2002C60E6 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 84167850161B56C4002C60E6 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 84C337971698B693009B8177 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 84C337AB1698BC5C009B8177 /* IOKit.framework in Frameworks */, + 84C337AA1698BC38009B8177 /* CoreFoundation.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 84F424DD161B59E500777765 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 841FEF7E16539DDF00A4D4C8 /* IOKit.framework in Frameworks */, + 84F424E3161B59E500777765 /* Cocoa.framework in Frameworks */, + 84F424E4161B59E500777765 /* PreferencePanes.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 84F424F8161B5A4900777765 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 84833FCD161BA27800845294 /* IOKit.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 84167806161B55B2002C60E6 = { + isa = PBXGroup; + children = ( + 844952F1169A2696003DA49F /* makefile */, + 84833FC0161B636900845294 /* Common */, + 84167818161B55B2002C60E6 /* VoodooPS2Controller */, + 8416782E161B5613002C60E6 /* VoodooPS2Keyboard */, + 84167856161B56C4002C60E6 /* VoodooPS2Trackpad */, + 84167815161B55B2002C60E6 /* Frameworks */, + ); + sourceTree = ""; + }; + 84167815161B55B2002C60E6 /* Frameworks */ = { + isa = PBXGroup; + children = ( + 84C337A91698BC38009B8177 /* CoreFoundation.framework */, + 84F424C3161B593D00777765 /* Cocoa.framework */, + 84F424C5161B593D00777765 /* PreferencePanes.framework */, + 84167816161B55B2002C60E6 /* Other Frameworks */, + C3F4FFB4CAD4F7E3223541CC /* Products */, + ); + name = Frameworks; + sourceTree = ""; + }; + 84167816161B55B2002C60E6 /* Other Frameworks */ = { + isa = PBXGroup; + children = ( + 84833FCC161BA27700845294 /* IOKit.framework */, + 84167817161B55B2002C60E6 /* Kernel.framework */, + 84F424C7161B593D00777765 /* AppKit.framework */, + 84F424C8161B593D00777765 /* CoreData.framework */, + 84F424C9161B593D00777765 /* Foundation.framework */, + ); + name = "Other Frameworks"; + sourceTree = ""; + }; + 84167818161B55B2002C60E6 /* VoodooPS2Controller */ = { + isa = PBXGroup; + children = ( + 84DD197A162D496E0044D061 /* AppleACPIPS2Nub.h */, + 84DD1979162D496E0044D061 /* AppleACPIPS2Nub.cpp */, + 840F104916EFE42600E8C116 /* ApplePS2Device.cpp */, + 84833F9E161B627D00845294 /* ApplePS2KeyboardDevice.cpp */, + 84833FA0161B627D00845294 /* ApplePS2MouseDevice.cpp */, + 8416781E161B55B2002C60E6 /* VoodooPS2Controller.h */, + 8416781F161B55B2002C60E6 /* VoodooPS2Controller.cpp */, + 84167819161B55B2002C60E6 /* Supporting Files */, + ); + path = VoodooPS2Controller; + sourceTree = ""; + }; + 84167819161B55B2002C60E6 /* Supporting Files */ = { + isa = PBXGroup; + children = ( + 84045664161E3AF900D74D7F /* InfoPlist.strings */, + 8416781A161B55B2002C60E6 /* VoodooPS2Controller-Info.plist */, + 84167821161B55B2002C60E6 /* VoodooPS2Controller-Prefix.pch */, + ); + name = "Supporting Files"; + sourceTree = ""; + }; + 8416782E161B5613002C60E6 /* VoodooPS2Keyboard */ = { + isa = PBXGroup; + children = ( + 84833FA9161B629500845294 /* ApplePS2ToADBMap.h */, + 84167834161B5613002C60E6 /* VoodooPS2Keyboard.h */, + 84167835161B5613002C60E6 /* VoodooPS2Keyboard.cpp */, + 8416782F161B5613002C60E6 /* Supporting Files */, + ); + path = VoodooPS2Keyboard; + sourceTree = ""; + }; + 8416782F161B5613002C60E6 /* Supporting Files */ = { + isa = PBXGroup; + children = ( + 84045661161E3AD800D74D7F /* InfoPlist.strings */, + 84167830161B5613002C60E6 /* VoodooPS2Keyboard-Info.plist */, + 8437048016284F66005B3C76 /* VoodooPS2Keyboard-RemapFN-Info.plist */, + 8441070016D4F68A0063F063 /* VoodooPS2Keyboard-Breakless-Info.plist */, + 84167837161B5613002C60E6 /* VoodooPS2Keyboard-Prefix.pch */, + ); + name = "Supporting Files"; + sourceTree = ""; + }; + 84167856161B56C4002C60E6 /* VoodooPS2Trackpad */ = { + isa = PBXGroup; + children = ( + 84833FAC161B62A900845294 /* alps.h */, + 84833FAB161B62A900845294 /* alps.cpp */, + 84167857161B56C4002C60E6 /* Supporting Files */, + C3F4F859C067FD563476F515 /* VoodooPS2TouchPadBase.h */, + C3F4F41B76902F9877062D93 /* VoodooPS2TouchPadBase.cpp */, + BA560D351734DFF100914439 /* Decay.h */, + ); + path = VoodooPS2Trackpad; + sourceTree = ""; + }; + 84167857161B56C4002C60E6 /* Supporting Files */ = { + isa = PBXGroup; + children = ( + 8404565B161E3AAF00D74D7F /* InfoPlist.strings */, + 84167858161B56C4002C60E6 /* VoodooPS2Trackpad-Info.plist */, + 8416785F161B56C4002C60E6 /* VoodooPS2Trackpad-Prefix.pch */, + ); + name = "Supporting Files"; + sourceTree = ""; + }; + 84833FC0161B636900845294 /* Common */ = { + isa = PBXGroup; + children = ( + 84833F9D161B627D00845294 /* ApplePS2Device.h */, + 84833F9F161B627D00845294 /* ApplePS2KeyboardDevice.h */, + 84833FA1161B627D00845294 /* ApplePS2MouseDevice.h */, + 84E9BAC816BE4C1300EEEB63 /* new_kext.h */, + 84E9BACA16BE4CB300EEEB63 /* new_kext.cpp */, + ); + name = Common; + sourceTree = ""; + }; + C3F4FFB4CAD4F7E3223541CC /* Products */ = { + isa = PBXGroup; + children = ( + C3F4FA022A4265DD37A85F4D /* VoodooPS2Controller.kext */, + C3F4FAC34E9069A684BFE9E1 /* VoodooPS2Keyboard.kext */, + C3F4F603A234F724795A2FBD /* VoodooPS2Trackpad.kext */, + ); + name = Products; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXHeadersBuildPhase section */ + 8416780F161B55B2002C60E6 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + 84833FA3161B627D00845294 /* ApplePS2Device.h in Headers */, + 84833FA5161B627D00845294 /* ApplePS2KeyboardDevice.h in Headers */, + 84833FA7161B627D00845294 /* ApplePS2MouseDevice.h in Headers */, + 84833FC3161B6A7E00845294 /* VoodooPS2Controller.h in Headers */, + 84DD197C162D496E0044D061 /* AppleACPIPS2Nub.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 84167829161B5613002C60E6 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + 84833FAA161B629500845294 /* ApplePS2ToADBMap.h in Headers */, + 84833FC2161B69C700845294 /* VoodooPS2Keyboard.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 8416783D161B56A2002C60E6 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 84167851161B56C4002C60E6 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + 84833FB2161B62A900845294 /* alps.h in Headers */, + BA5C70D017338E8600E30E1A /* VoodooPS2TouchPadBase.h in Headers */, + BA560D361734DFF100914439 /* Decay.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 84F424DE161B59E500777765 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXHeadersBuildPhase section */ + +/* Begin PBXNativeTarget section */ + 84167812161B55B2002C60E6 /* VoodooPS2Controller */ = { + isa = PBXNativeTarget; + buildConfigurationList = 84167824161B55B2002C60E6 /* Build configuration list for PBXNativeTarget "VoodooPS2Controller" */; + buildPhases = ( + 8416780D161B55B2002C60E6 /* Sources */, + 8416780E161B55B2002C60E6 /* Frameworks */, + 8416780F161B55B2002C60E6 /* Headers */, + 84167810161B55B2002C60E6 /* Resources */, + 84167811161B55B2002C60E6 /* Rez */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = VoodooPS2Controller; + productName = VoodooPS2Controller; + productType = "com.apple.product-type.kernel-extension"; + }; + 8416782C161B5613002C60E6 /* VoodooPS2Keyboard */ = { + isa = PBXNativeTarget; + buildConfigurationList = 84167838161B5613002C60E6 /* Build configuration list for PBXNativeTarget "VoodooPS2Keyboard" */; + buildPhases = ( + 84167827161B5613002C60E6 /* Sources */, + 84167828161B5613002C60E6 /* Frameworks */, + 84167829161B5613002C60E6 /* Headers */, + 8416782A161B5613002C60E6 /* Resources */, + 8416782B161B5613002C60E6 /* Rez */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = VoodooPS2Keyboard; + productName = VoodooPS2Keyboard; + productType = "com.apple.product-type.kernel-extension"; + }; + 84167840161B56A2002C60E6 /* VoodooPS2Mouse */ = { + isa = PBXNativeTarget; + buildConfigurationList = 8416784C161B56A2002C60E6 /* Build configuration list for PBXNativeTarget "VoodooPS2Mouse" */; + buildPhases = ( + 8416783B161B56A2002C60E6 /* Sources */, + 8416783C161B56A2002C60E6 /* Frameworks */, + 8416783D161B56A2002C60E6 /* Headers */, + 8416783E161B56A2002C60E6 /* Resources */, + 8416783F161B56A2002C60E6 /* Rez */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = VoodooPS2Mouse; + productName = VoodooPS2Mouse; + productType = "com.apple.product-type.kernel-extension"; + }; + 84167854161B56C4002C60E6 /* VoodooPS2Trackpad */ = { + isa = PBXNativeTarget; + buildConfigurationList = 84167860161B56C4002C60E6 /* Build configuration list for PBXNativeTarget "VoodooPS2Trackpad" */; + buildPhases = ( + 8416784F161B56C4002C60E6 /* Sources */, + 84167850161B56C4002C60E6 /* Frameworks */, + 84167851161B56C4002C60E6 /* Headers */, + 84167852161B56C4002C60E6 /* Resources */, + 84167853161B56C4002C60E6 /* Rez */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = VoodooPS2Trackpad; + productName = VoodooPS2Trackpad; + productType = "com.apple.product-type.kernel-extension"; + }; + 84C337991698B693009B8177 /* VoodooPS2Daemon */ = { + isa = PBXNativeTarget; + buildConfigurationList = 84C337A31698B693009B8177 /* Build configuration list for PBXNativeTarget "VoodooPS2Daemon" */; + buildPhases = ( + 84C337961698B693009B8177 /* Sources */, + 84C337971698B693009B8177 /* Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = VoodooPS2Daemon; + productName = VoodooPS2Daemon; + productType = "com.apple.product-type.tool"; + }; + 84F424E1161B59E500777765 /* VoodooPS2synapticsPane */ = { + isa = PBXNativeTarget; + buildConfigurationList = 84F424F4161B59E500777765 /* Build configuration list for PBXNativeTarget "VoodooPS2synapticsPane" */; + buildPhases = ( + 84F424DC161B59E500777765 /* Sources */, + 84F424DD161B59E500777765 /* Frameworks */, + 84F424DE161B59E500777765 /* Headers */, + 84F424DF161B59E500777765 /* Resources */, + 84F424E0161B59E500777765 /* Rez */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = VoodooPS2synapticsPane; + productName = VoodooPS2synapticsPane; + productType = "com.apple.product-type.bundle"; + }; + 84F424FA161B5A4900777765 /* synapticsconfigload */ = { + isa = PBXNativeTarget; + buildConfigurationList = 84F42502161B5A4900777765 /* Build configuration list for PBXNativeTarget "synapticsconfigload" */; + buildPhases = ( + 84F424F7161B5A4900777765 /* Sources */, + 84F424F8161B5A4900777765 /* Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = synapticsconfigload; + productName = synapticsconfigload; + productType = "com.apple.product-type.tool"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 84167808161B55B2002C60E6 /* Project object */ = { + isa = PBXProject; + attributes = { + LastUpgradeCheck = 0810; + ORGANIZATIONNAME = rehabman; + }; + buildConfigurationList = 8416780B161B55B2002C60E6 /* Build configuration list for PBXProject "VoodooPS2Controller" */; + compatibilityVersion = "Xcode 8.0"; + developmentRegion = English; + hasScannedForEncodings = 0; + knownRegions = ( + en, + English, + ); + mainGroup = 84167806161B55B2002C60E6; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 84167812161B55B2002C60E6 /* VoodooPS2Controller */, + 8416782C161B5613002C60E6 /* VoodooPS2Keyboard */, + 84167840161B56A2002C60E6 /* VoodooPS2Mouse */, + 84167854161B56C4002C60E6 /* VoodooPS2Trackpad */, + 84F424E1161B59E500777765 /* VoodooPS2synapticsPane */, + 84F424FA161B5A4900777765 /* synapticsconfigload */, + 84C337991698B693009B8177 /* VoodooPS2Daemon */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 84167810161B55B2002C60E6 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 8416782A161B5613002C60E6 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 8416783E161B56A2002C60E6 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 84167852161B56C4002C60E6 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 84F424DF161B59E500777765 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXRezBuildPhase section */ + 84167811161B55B2002C60E6 /* Rez */ = { + isa = PBXRezBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 8416782B161B5613002C60E6 /* Rez */ = { + isa = PBXRezBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 8416783F161B56A2002C60E6 /* Rez */ = { + isa = PBXRezBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 84167853161B56C4002C60E6 /* Rez */ = { + isa = PBXRezBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 84F424E0161B59E500777765 /* Rez */ = { + isa = PBXRezBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXRezBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 8416780D161B55B2002C60E6 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 84167820161B55B2002C60E6 /* VoodooPS2Controller.cpp in Sources */, + 84DD197B162D496E0044D061 /* AppleACPIPS2Nub.cpp in Sources */, + 840F104A16EFE42600E8C116 /* ApplePS2Device.cpp in Sources */, + 84EB0AE316F0AD9300016108 /* ApplePS2KeyboardDevice.cpp in Sources */, + 84EB0AE516F0AD9600016108 /* ApplePS2MouseDevice.cpp in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 84167827161B5613002C60E6 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 84167836161B5613002C60E6 /* VoodooPS2Keyboard.cpp in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 8416783B161B56A2002C60E6 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 8416784F161B56C4002C60E6 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 84833FB1161B62A900845294 /* alps.cpp in Sources */, + BA5C70CF17338E7000E30E1A /* VoodooPS2TouchPadBase.cpp in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 84C337961698B693009B8177 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 84F424DC161B59E500777765 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 84F424F7161B5A4900777765 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXVariantGroup section */ + 8404565B161E3AAF00D74D7F /* InfoPlist.strings */ = { + isa = PBXVariantGroup; + children = ( + 8404565C161E3AAF00D74D7F /* en */, + ); + name = InfoPlist.strings; + sourceTree = ""; + }; + 84045661161E3AD800D74D7F /* InfoPlist.strings */ = { + isa = PBXVariantGroup; + children = ( + 84045662161E3AD800D74D7F /* en */, + ); + name = InfoPlist.strings; + sourceTree = ""; + }; + 84045664161E3AF900D74D7F /* InfoPlist.strings */ = { + isa = PBXVariantGroup; + children = ( + 84045665161E3AF900D74D7F /* en */, + ); + name = InfoPlist.strings; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + 84167822161B55B2002C60E6 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; + CLANG_USE_OPTIMIZATION_PROFILE = NO; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CURRENT_PROJECT_VERSION = 6.0.0; + DEBUG_INFORMATION_FORMAT = dwarf; + DEPLOYMENT_LOCATION = NO; + DEPLOYMENT_POSTPROCESSING = YES; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = NO; + GCC_INLINES_ARE_PRIVATE_EXTERN = YES; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = fast; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "DEBUG_MSG=1", + "MACH_ASSERT=1", + "$(inherited)", + ); + GCC_SYMBOLS_PRIVATE_EXTERN = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + LLVM_LTO = YES_THIN; + "LLVM_LTO[arch=x86_64]" = YES_THIN; + MACOSX_DEPLOYMENT_TARGET = 10.6; + MODULE_VERSION = 6.0.0; + ONLY_ACTIVE_ARCH = YES; + "OTHER_LDFLAGS[arch=x86_64]" = "-dead_strip"; + PRODUCT_NAME = VoodooPS2Controller; + SDKROOT = macosx; + }; + name = Debug; + }; + 84167823161B55B2002C60E6 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; + CLANG_USE_OPTIMIZATION_PROFILE = NO; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CURRENT_PROJECT_VERSION = 6.0.0; + DEBUG_INFORMATION_FORMAT = dwarf; + DEPLOYMENT_POSTPROCESSING = YES; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = NO; + GCC_INLINES_ARE_PRIVATE_EXTERN = YES; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = fast; + GCC_SYMBOLS_PRIVATE_EXTERN = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + LLVM_LTO = YES_THIN; + "LLVM_LTO[arch=x86_64]" = YES_THIN; + MACOSX_DEPLOYMENT_TARGET = 10.6; + MODULE_VERSION = 6.0.0; + ONLY_ACTIVE_ARCH = YES; + "OTHER_LDFLAGS[arch=x86_64]" = "-dead_strip"; + PRODUCT_NAME = VoodooPS2Controller; + SDKROOT = macosx; + }; + name = Release; + }; + 84167825161B55B2002C60E6 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + COMBINE_HIDPI_IMAGES = YES; + GCC_PRECOMPILE_PREFIX_HEADER = YES; + GCC_PREFIX_HEADER = "VoodooPS2Controller/VoodooPS2Controller-Prefix.pch"; + INFOPLIST_FILE = "VoodooPS2Controller/VoodooPS2Controller-Info.plist"; + MODULE_NAME = com.rehabman.driver.VoodooPS2Controller; + OTHER_CFLAGS = "-fno-stack-protector"; + PRODUCT_BUNDLE_IDENTIFIER = org.rehabman.voodoo.driver.PS2Controller; + PRODUCT_NAME = "$(TARGET_NAME)"; + STRIP_INSTALLED_PRODUCT = YES; + STRIP_STYLE = "non-global"; + }; + name = Debug; + }; + 84167826161B55B2002C60E6 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + COMBINE_HIDPI_IMAGES = YES; + GCC_PRECOMPILE_PREFIX_HEADER = YES; + GCC_PREFIX_HEADER = "VoodooPS2Controller/VoodooPS2Controller-Prefix.pch"; + INFOPLIST_FILE = "VoodooPS2Controller/VoodooPS2Controller-Info.plist"; + MODULE_NAME = com.rehabman.driver.VoodooPS2Controller; + OTHER_CFLAGS = "-fno-stack-protector"; + PRODUCT_BUNDLE_IDENTIFIER = org.rehabman.voodoo.driver.PS2Controller; + PRODUCT_NAME = "$(TARGET_NAME)"; + STRIP_INSTALLED_PRODUCT = YES; + STRIP_STYLE = "non-global"; + }; + name = Release; + }; + 84167839161B5613002C60E6 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + COMBINE_HIDPI_IMAGES = YES; + CONFIGURATION_BUILD_DIR = "$(BUILD_DIR)/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)/VoodooPS2Controller.kext/Contents/PlugIns"; + GCC_PRECOMPILE_PREFIX_HEADER = YES; + GCC_PREFIX_HEADER = "VoodooPS2Keyboard/VoodooPS2Keyboard-Prefix.pch"; + INFOPLIST_FILE = "VoodooPS2Keyboard/VoodooPS2Keyboard-Info.plist"; + MODULE_NAME = com.rehabman.driver.VoodooPS2Keyboard; + OTHER_CFLAGS = "-fno-stack-protector"; + PRODUCT_BUNDLE_IDENTIFIER = org.rehabman.voodoo.driver.PS2Keyboard; + PRODUCT_NAME = "$(TARGET_NAME)"; + STRIP_INSTALLED_PRODUCT = YES; + STRIP_STYLE = "non-global"; + }; + name = Debug; + }; + 8416783A161B5613002C60E6 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + COMBINE_HIDPI_IMAGES = YES; + CONFIGURATION_BUILD_DIR = "$(BUILD_DIR)/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)/VoodooPS2Controller.kext/Contents/PlugIns"; + GCC_PRECOMPILE_PREFIX_HEADER = YES; + GCC_PREFIX_HEADER = "VoodooPS2Keyboard/VoodooPS2Keyboard-Prefix.pch"; + INFOPLIST_FILE = "VoodooPS2Keyboard/VoodooPS2Keyboard-Info.plist"; + MODULE_NAME = com.rehabman.driver.VoodooPS2Keyboard; + OTHER_CFLAGS = "-fno-stack-protector"; + PRODUCT_BUNDLE_IDENTIFIER = org.rehabman.voodoo.driver.PS2Keyboard; + PRODUCT_NAME = "$(TARGET_NAME)"; + STRIP_INSTALLED_PRODUCT = YES; + STRIP_STYLE = "non-global"; + }; + name = Release; + }; + 8416784D161B56A2002C60E6 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + COMBINE_HIDPI_IMAGES = YES; + CONFIGURATION_BUILD_DIR = "$(BUILD_DIR)/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)/VoodooPS2Controller.kext/Contents/PlugIns"; + GCC_PRECOMPILE_PREFIX_HEADER = YES; + GCC_PREFIX_HEADER = "VoodooPS2Mouse/VoodooPS2Mouse-Prefix.pch"; + INFOPLIST_FILE = "VoodooPS2Mouse/VoodooPS2Mouse-Info.plist"; + MODULE_NAME = com.rehabman.driver.VoodooPS2Mouse; + OTHER_CFLAGS = "-fno-stack-protector"; + PRODUCT_BUNDLE_IDENTIFIER = org.rehabman.voodoo.driver.PS2Mouse; + PRODUCT_NAME = "$(TARGET_NAME)"; + STRIP_INSTALLED_PRODUCT = YES; + STRIP_STYLE = "non-global"; + }; + name = Debug; + }; + 8416784E161B56A2002C60E6 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + COMBINE_HIDPI_IMAGES = YES; + CONFIGURATION_BUILD_DIR = "$(BUILD_DIR)/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)/VoodooPS2Controller.kext/Contents/PlugIns"; + GCC_PRECOMPILE_PREFIX_HEADER = YES; + GCC_PREFIX_HEADER = "VoodooPS2Mouse/VoodooPS2Mouse-Prefix.pch"; + INFOPLIST_FILE = "VoodooPS2Mouse/VoodooPS2Mouse-Info.plist"; + MODULE_NAME = com.rehabman.driver.VoodooPS2Mouse; + OTHER_CFLAGS = "-fno-stack-protector"; + PRODUCT_BUNDLE_IDENTIFIER = org.rehabman.voodoo.driver.PS2Mouse; + PRODUCT_NAME = "$(TARGET_NAME)"; + STRIP_INSTALLED_PRODUCT = YES; + STRIP_STYLE = "non-global"; + }; + name = Release; + }; + 84167861161B56C4002C60E6 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + COMBINE_HIDPI_IMAGES = YES; + CONFIGURATION_BUILD_DIR = "$(BUILD_DIR)/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)/VoodooPS2Controller.kext/Contents/PlugIns"; + GCC_PRECOMPILE_PREFIX_HEADER = YES; + GCC_PREFIX_HEADER = "VoodooPS2Trackpad/VoodooPS2Trackpad-Prefix.pch"; + INFOPLIST_FILE = "VoodooPS2Trackpad/VoodooPS2Trackpad-Info.plist"; + MODULE_NAME = com.rehabman.driver.VoodooPS2Trackpad; + OTHER_CFLAGS = "-fno-stack-protector"; + PRODUCT_BUNDLE_IDENTIFIER = org.rehabman.voodoo.driver.PS2Trackpad; + PRODUCT_NAME = "$(TARGET_NAME)"; + STRIP_INSTALLED_PRODUCT = YES; + STRIP_STYLE = "non-global"; + }; + name = Debug; + }; + 84167862161B56C4002C60E6 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + COMBINE_HIDPI_IMAGES = YES; + CONFIGURATION_BUILD_DIR = "$(BUILD_DIR)/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)/VoodooPS2Controller.kext/Contents/PlugIns"; + GCC_PRECOMPILE_PREFIX_HEADER = YES; + GCC_PREFIX_HEADER = "VoodooPS2Trackpad/VoodooPS2Trackpad-Prefix.pch"; + INFOPLIST_FILE = "VoodooPS2Trackpad/VoodooPS2Trackpad-Info.plist"; + MODULE_NAME = com.rehabman.driver.VoodooPS2Trackpad; + OTHER_CFLAGS = "-fno-stack-protector"; + PRODUCT_BUNDLE_IDENTIFIER = org.rehabman.voodoo.driver.PS2Trackpad; + PRODUCT_NAME = "$(TARGET_NAME)"; + STRIP_INSTALLED_PRODUCT = YES; + STRIP_STYLE = "non-global"; + }; + name = Release; + }; + 84C337A11698B693009B8177 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + GCC_PREPROCESSOR_DEFINITIONS = "$(inherited)"; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Debug; + }; + 84C337A21698B693009B8177 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Release; + }; + 84F424F5161B59E500777765 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + COMBINE_HIDPI_IMAGES = YES; + GCC_PRECOMPILE_PREFIX_HEADER = YES; + GCC_PREFIX_HEADER = "VoodooPS2synapticsPane/VoodooPS2synapticsPane-Prefix.pch"; + GCC_PREPROCESSOR_DEFINITIONS = "$(inherited)"; + INFOPLIST_FILE = "VoodooPS2synapticsPane/VoodooPS2synapticsPane-Info.plist"; + INSTALL_PATH = "$(HOME)/Library/PreferencePanes"; + PRODUCT_BUNDLE_IDENTIFIER = "com.rehabman.${PRODUCT_NAME:identifier}"; + PRODUCT_NAME = "$(TARGET_NAME)"; + WRAPPER_EXTENSION = prefPane; + }; + name = Debug; + }; + 84F424F6161B59E500777765 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + COMBINE_HIDPI_IMAGES = YES; + GCC_PRECOMPILE_PREFIX_HEADER = YES; + GCC_PREFIX_HEADER = "VoodooPS2synapticsPane/VoodooPS2synapticsPane-Prefix.pch"; + INFOPLIST_FILE = "VoodooPS2synapticsPane/VoodooPS2synapticsPane-Info.plist"; + INSTALL_PATH = "$(HOME)/Library/PreferencePanes"; + PRODUCT_BUNDLE_IDENTIFIER = "com.rehabman.${PRODUCT_NAME:identifier}"; + PRODUCT_NAME = "$(TARGET_NAME)"; + WRAPPER_EXTENSION = prefPane; + }; + name = Release; + }; + 84F42503161B5A4900777765 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + GCC_PREPROCESSOR_DEFINITIONS = "$(inherited)"; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Debug; + }; + 84F42504161B5A4900777765 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 8416780B161B55B2002C60E6 /* Build configuration list for PBXProject "VoodooPS2Controller" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 84167822161B55B2002C60E6 /* Debug */, + 84167823161B55B2002C60E6 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 84167824161B55B2002C60E6 /* Build configuration list for PBXNativeTarget "VoodooPS2Controller" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 84167825161B55B2002C60E6 /* Debug */, + 84167826161B55B2002C60E6 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 84167838161B5613002C60E6 /* Build configuration list for PBXNativeTarget "VoodooPS2Keyboard" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 84167839161B5613002C60E6 /* Debug */, + 8416783A161B5613002C60E6 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 8416784C161B56A2002C60E6 /* Build configuration list for PBXNativeTarget "VoodooPS2Mouse" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 8416784D161B56A2002C60E6 /* Debug */, + 8416784E161B56A2002C60E6 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 84167860161B56C4002C60E6 /* Build configuration list for PBXNativeTarget "VoodooPS2Trackpad" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 84167861161B56C4002C60E6 /* Debug */, + 84167862161B56C4002C60E6 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 84C337A31698B693009B8177 /* Build configuration list for PBXNativeTarget "VoodooPS2Daemon" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 84C337A11698B693009B8177 /* Debug */, + 84C337A21698B693009B8177 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 84F424F4161B59E500777765 /* Build configuration list for PBXNativeTarget "VoodooPS2synapticsPane" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 84F424F5161B59E500777765 /* Debug */, + 84F424F6161B59E500777765 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 84F42502161B5A4900777765 /* Build configuration list for PBXNativeTarget "synapticsconfigload" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 84F42503161B5A4900777765 /* Debug */, + 84F42504161B5A4900777765 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 84167808161B55B2002C60E6 /* Project object */; +} diff --git a/VoodooPS2Controller.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/VoodooPS2Controller.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..37e8574 --- /dev/null +++ b/VoodooPS2Controller.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/VoodooPS2Controller.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/VoodooPS2Controller.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings new file mode 100644 index 0000000..54782e3 --- /dev/null +++ b/VoodooPS2Controller.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings @@ -0,0 +1,8 @@ + + + + + IDEWorkspaceSharedSettings_AutocreateContextsIfNeeded + + + diff --git a/VoodooPS2Controller.xcodeproj/project.xcworkspace/xcuserdata/ahmad.xcuserdatad/UserInterfaceState.xcuserstate b/VoodooPS2Controller.xcodeproj/project.xcworkspace/xcuserdata/ahmad.xcuserdatad/UserInterfaceState.xcuserstate new file mode 100644 index 0000000..6194101 Binary files /dev/null and b/VoodooPS2Controller.xcodeproj/project.xcworkspace/xcuserdata/ahmad.xcuserdatad/UserInterfaceState.xcuserstate differ diff --git a/VoodooPS2Controller.xcodeproj/project.xcworkspace/xcuserdata/ahmad.xcuserdatad/WorkspaceSettings.xcsettings b/VoodooPS2Controller.xcodeproj/project.xcworkspace/xcuserdata/ahmad.xcuserdatad/WorkspaceSettings.xcsettings new file mode 100644 index 0000000..f61e19c --- /dev/null +++ b/VoodooPS2Controller.xcodeproj/project.xcworkspace/xcuserdata/ahmad.xcuserdatad/WorkspaceSettings.xcsettings @@ -0,0 +1,22 @@ + + + + + BuildLocationStyle + UseAppPreferences + CustomBuildLocationType + RelativeToDerivedData + DerivedDataCustomLocation + DerivedData + DerivedDataLocationStyle + WorkspaceRelativePath + HasAskedToTakeAutomaticSnapshotBeforeSignificantChanges + + IssueFilterStyle + ShowActiveSchemeOnly + LiveSourceIssuesEnabled + + SnapshotAutomaticallyBeforeSignificantChanges + + + diff --git a/VoodooPS2Controller.xcodeproj/xcshareddata/xcschemes/All Kext.xcscheme b/VoodooPS2Controller.xcodeproj/xcshareddata/xcschemes/All Kext.xcscheme new file mode 100644 index 0000000..ac448f3 --- /dev/null +++ b/VoodooPS2Controller.xcodeproj/xcshareddata/xcschemes/All Kext.xcscheme @@ -0,0 +1,113 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/VoodooPS2Controller.xcodeproj/xcshareddata/xcschemes/All.xcscheme b/VoodooPS2Controller.xcodeproj/xcshareddata/xcschemes/All.xcscheme new file mode 100644 index 0000000..2a1ada0 --- /dev/null +++ b/VoodooPS2Controller.xcodeproj/xcshareddata/xcschemes/All.xcscheme @@ -0,0 +1,117 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/VoodooPS2Controller.xcodeproj/xcshareddata/xcschemes/VoodooPS2Controller.xcscheme b/VoodooPS2Controller.xcodeproj/xcshareddata/xcschemes/VoodooPS2Controller.xcscheme new file mode 100644 index 0000000..2333d23 --- /dev/null +++ b/VoodooPS2Controller.xcodeproj/xcshareddata/xcschemes/VoodooPS2Controller.xcscheme @@ -0,0 +1,141 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/VoodooPS2Controller.xcodeproj/xcshareddata/xcschemes/VoodooPS2Keyboard.xcscheme b/VoodooPS2Controller.xcodeproj/xcshareddata/xcschemes/VoodooPS2Keyboard.xcscheme new file mode 100644 index 0000000..5ec8ecb --- /dev/null +++ b/VoodooPS2Controller.xcodeproj/xcshareddata/xcschemes/VoodooPS2Keyboard.xcscheme @@ -0,0 +1,71 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/VoodooPS2Controller.xcodeproj/xcshareddata/xcschemes/VoodooPS2Trackpad.xcscheme b/VoodooPS2Controller.xcodeproj/xcshareddata/xcschemes/VoodooPS2Trackpad.xcscheme new file mode 100644 index 0000000..098dae6 --- /dev/null +++ b/VoodooPS2Controller.xcodeproj/xcshareddata/xcschemes/VoodooPS2Trackpad.xcscheme @@ -0,0 +1,71 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/VoodooPS2Controller.xcodeproj/xcuserdata/ahmad.xcuserdatad/xcschemes/xcschememanagement.plist b/VoodooPS2Controller.xcodeproj/xcuserdata/ahmad.xcuserdatad/xcschemes/xcschememanagement.plist new file mode 100644 index 0000000..376d621 --- /dev/null +++ b/VoodooPS2Controller.xcodeproj/xcuserdata/ahmad.xcuserdatad/xcschemes/xcschememanagement.plist @@ -0,0 +1,44 @@ + + + + + SuppressBuildableAutocreation + + 84167812161B55B2002C60E6 + + primary + + + 8416782C161B5613002C60E6 + + primary + + + 84167840161B56A2002C60E6 + + primary + + + 84167854161B56C4002C60E6 + + primary + + + 84C337991698B693009B8177 + + primary + + + 84F424E1161B59E500777765 + + primary + + + 84F424FA161B5A4900777765 + + primary + + + + + diff --git a/VoodooPS2Controller/AppleACPIPS2Nub.cpp b/VoodooPS2Controller/AppleACPIPS2Nub.cpp new file mode 100644 index 0000000..7a50fe5 --- /dev/null +++ b/VoodooPS2Controller/AppleACPIPS2Nub.cpp @@ -0,0 +1,228 @@ +/* + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +/*! @file AppleACPIPS2Nub.cpp + @abstract AppleACPIPS2Nub class implementation + @discussion + Implements the ACPI PS/2 nub for ApplePS2Controller.kext. + Reverse-engineered from the Darwin 8 binary ACPI kext. + Copyright 2007 David Elliott + */ + +#include "AppleACPIPS2Nub.h" + +#if 0 +#define DEBUG_LOG(args...) IOLog(args) +#else +#define DEBUG_LOG(args...) +#endif + +static IOPMPowerState myTwoStates[2] = { + {1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {1, kIOPMPowerOn, kIOPMPowerOn, kIOPMPowerOn, 0, 0, 0, 0, 0, 0, 0, 0} +}; + +OSDefineMetaClassAndStructors(AppleACPIPS2Nub, IOPlatformDevice); + +// We could simply ask for the PE rather than importing the global +// from AppleACPIPlatformExpert.kext +// extern IOPlatformExpert *gAppleACPIPlatformExpert; + +bool AppleACPIPS2Nub::start(IOService *provider) +{ + if (!super::start(provider)) + return false; + + DEBUG_LOG("AppleACPIPS2Nub::start: provider=%p\n", provider); + + /* Initialize our interrupt controller/specifier i-vars */ + m_interruptControllers = OSArray::withCapacity(2); + m_interruptSpecifiers = OSArray::withCapacity(2); + if(m_interruptControllers == NULL || m_interruptSpecifiers == NULL) + return false; + + /* Merge in the keyboard (primary) provider interrupt properties */ + mergeInterruptProperties(provider, LEGACY_KEYBOARD_IRQ); + + /* Initialize and register our power management properties */ + PMinit(); + registerPowerDriver(this, myTwoStates, 2); + provider->joinPMtree(this); + + /* Find the mouse provider */ + m_mouseProvider = findMouseDevice(); + if(m_mouseProvider != NULL) + { + DEBUG_LOG("AppleACPIPS2Nub::start: Found mouse PNP device\n"); + if(attach(m_mouseProvider)) + { + mergeInterruptProperties(m_mouseProvider, LEGACY_MOUSE_IRQ); + if(m_mouseProvider->inPlane(gIOPowerPlane)) + { + m_mouseProvider->joinPMtree(this); + } + } + } + + /* Set our interrupt properties in the IO registry */ + if(m_interruptControllers->getCount() != 0 && m_interruptSpecifiers->getCount() != 0) + { + setProperty(gIOInterruptControllersKey, m_interruptControllers); + setProperty(gIOInterruptSpecifiersKey, m_interruptSpecifiers); + } + + /* Release the arrays we allocated. Our properties dictionary has them retained */ + m_interruptControllers->release(); + m_interruptControllers = NULL; + m_interruptSpecifiers->release(); + m_interruptSpecifiers = NULL; + + /* Make ourselves the ps2controller nub and register so ApplePS2Controller can find us. */ + setName("ps2controller"); + registerService(); + + DEBUG_LOG("AppleACPIPS2Nub::start: startup complete\n"); + + return true; +} + +IOService *AppleACPIPS2Nub::findMouseDevice() +{ + OSObject *prop = getProperty("MouseNameMatch"); + /* Search from the root of the ACPI plane for the mouse PNP nub */ + IORegistryIterator *i = IORegistryIterator::iterateOver(gIOACPIPlane, kIORegistryIterateRecursively); + IORegistryEntry *entry; + if(i != NULL) + { + while((entry = i->getNextObject())) + { + if(entry->compareNames(prop)) + break; + } + i->release(); + } + else + entry = NULL; + return OSDynamicCast(IOService, entry); +} + +void AppleACPIPS2Nub::mergeInterruptProperties(IOService *pnpProvider, long) +{ + /* Get the interrupt controllers/specifiers arrays from the provider, and make sure they + * exist and contain at least one entry. We assume they contain exactly one entry. + */ + OSArray *controllers = OSDynamicCast(OSArray,pnpProvider->getProperty(gIOInterruptControllersKey)); + OSArray *specifiers = OSDynamicCast(OSArray,pnpProvider->getProperty(gIOInterruptSpecifiersKey)); + if(controllers == NULL || specifiers == NULL) + return; + if(controllers->getCount() == 0 || specifiers->getCount() == 0) + return; + + /* Append the first object of each array into our own respective array */ + m_interruptControllers->setObject(controllers->getObject(0)); + m_interruptSpecifiers->setObject(specifiers->getObject(0)); +} + +IOReturn AppleACPIPS2Nub::registerInterrupt(int source, OSObject *target, IOInterruptAction handler, void *refCon) +{ + if(source == LEGACY_KEYBOARD_IRQ) + return super::registerInterrupt(0, target, handler, refCon); + else if(source == LEGACY_MOUSE_IRQ) + return super::registerInterrupt(1, target, handler, refCon); + else + return kIOReturnBadArgument; +} + +IOReturn AppleACPIPS2Nub::unregisterInterrupt(int source) +{ + if(source == LEGACY_KEYBOARD_IRQ) + return super::unregisterInterrupt(0); + else if(source == LEGACY_MOUSE_IRQ) + return super::unregisterInterrupt(1); + else + return kIOReturnBadArgument; +} + +IOReturn AppleACPIPS2Nub::getInterruptType(int source, int *interruptType) +{ + if(source == LEGACY_KEYBOARD_IRQ) + return super::getInterruptType(0, interruptType); + else if(source == LEGACY_MOUSE_IRQ) + return super::getInterruptType(1, interruptType); + else + return kIOReturnBadArgument; +} + +IOReturn AppleACPIPS2Nub::enableInterrupt(int source) +{ + if(source == LEGACY_KEYBOARD_IRQ) + return super::enableInterrupt(0); + else if(source == LEGACY_MOUSE_IRQ) + return super::enableInterrupt(1); + else + return kIOReturnBadArgument; +} + +IOReturn AppleACPIPS2Nub::disableInterrupt(int source) +{ + if(source == LEGACY_KEYBOARD_IRQ) + return super::disableInterrupt(0); + else if(source == LEGACY_MOUSE_IRQ) + return super::disableInterrupt(1); + else + return kIOReturnBadArgument; +} + +bool AppleACPIPS2Nub::compareName( OSString * name, OSString ** matched ) const +{ +// return gAppleACPIPlatformExpert->compareNubName( this, name, matched ); + return( this->IORegistryEntry::compareName( name, matched )); +} + +IOReturn AppleACPIPS2Nub::getResources( void ) +{ +// return gAppleACPIPlatformExpert->getNubResources(this); + return( kIOReturnSuccess ); +} + +IOReturn AppleACPIPS2Nub::message( UInt32 type, IOService *provider, void *argument ) +{ + ////DEBUG_LOG("AppleACPIPS2Nub::message: type=%x, provider=%p, argument=%p\n", type, provider, argument); + + // forward to all interested sub-entries + IORegistryIterator *i = IORegistryIterator::iterateOver(this, gIOServicePlane, kIORegistryIterateRecursively); + IORegistryEntry *entry; + if(i != NULL) + { + while((entry = i->getNextObject())) + { + IOService* service = OSDynamicCast(IOService, entry); + if(service != NULL && service->getProperty(kDeliverNotifications)) + { + service->message(type, provider, argument); + } + } + i->release(); + } + + return( kIOReturnSuccess ); +} diff --git a/VoodooPS2Controller/AppleACPIPS2Nub.h b/VoodooPS2Controller/AppleACPIPS2Nub.h new file mode 100644 index 0000000..4192b13 --- /dev/null +++ b/VoodooPS2Controller/AppleACPIPS2Nub.h @@ -0,0 +1,150 @@ +/* + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +/*! @file AppleACPIPS2Nub.h + @abstract AppleACPIPS2Nub class definition + @discussion + Implements the ACPI PS/2 nub for ApplePS2Controller.kext. + Reverse-engineered from the Darwin 8 binary ACPI kext. + Copyright 2007 David Elliott + */ + +#ifndef __AppleACPIPS2Nub__ +#define __AppleACPIPS2Nub__ + +#include +#include +#include + +#define EXPORT __attribute__((visibility("default"))) + +#define kDeliverNotifications "RM,deliverNotifications" + +class IOPlatformExpert; + +/*! @class AppleACPIPS2Nub + @abstract Provides a nub that ApplePS2Controller can attach to + @discussion + The ApplePS2Controller driver is written to the nub provided by the + AppleI386PlatformExpert class which exposes the controller as it is found + on a legacy x86 machine. + + To make that kext work on an ACPI machine, a nub providing the same + service must exist. Previous releases of the official ACPI PE included + this class. Newer release do not. This implementation is intended + to be fully ABI compatible with the one Apple used to provide. + */ +class EXPORT AppleACPIPS2Nub: public IOPlatformDevice +{ + typedef IOPlatformDevice super; + OSDeclareDefaultStructors(AppleACPIPS2Nub); + +private: + /*! @field m_mouseProvider + @abstract Our second provider which provides the mouse nub + @discussion + We attach to the keyboard nub but we need to be attached to both the + keyboard and mouse nub in order to make ourselves a proper nub for + the ApplePS2Controller.kext driver. + */ + IOService *m_mouseProvider; + + /*! @field m_interruptControllers + @abstract Our array of interrupt controllers + */ + OSArray *m_interruptControllers; + + /*! @field m_interruptSpecifiers + @abstract Our array of interrupt specifiers + */ + OSArray *m_interruptSpecifiers; + + enum LegacyInterrupts + { + LEGACY_KEYBOARD_IRQ = 1, + LEGACY_MOUSE_IRQ = 12, + }; + +public: + virtual bool start(IOService *provider); + + /*! @method findMouseDevice + @abstract Locates the mouse nub in the IORegistry + */ + virtual IOService *findMouseDevice(); + + /*! @method mergeInterruptProperties + @abstract Merges the interrupt specifiers and controllers from our two providers + @param pnpProvider The provider nub + @discussion + This is called once for each of our providers. The interrupt controller and interrupt + specifier objects from the provider's arrays are appended to our arrays. + */ + virtual void mergeInterruptProperties(IOService *pnpProvider, long source); + + /*! @method registerInterrupt + @abstract Overriden to translate the legacy interrupt numbers to ours + @discussion + The legacy interrupts are 1 for the keyboard and 12 (0xc) for the mouse. + However, the base class code works off of the controller and specifier + objects in our IOInterruptControllers and IOInterruptSpecifiers keys. + Therefore, we must translate the keyboard interrupt (1) to the index + into our array (0) and the mouse interrupt (12) to the index into + our array (1). + + This has to be done for every *Interrupt* method + */ + virtual IOReturn registerInterrupt(int source, OSObject *target, + IOInterruptAction handler, + void *refCon = 0); + virtual IOReturn unregisterInterrupt(int source); + virtual IOReturn getInterruptType(int source, int *interruptType); + virtual IOReturn enableInterrupt(int source); + virtual IOReturn disableInterrupt(int source); + + /*! @method compareName + @abstract Overridden to call the IOPlatformExpert compareNubName method + @discussion + I have no idea why this is done, but the Apple code did it, so this + code does too. + */ + virtual bool compareName( OSString * name, OSString ** matched = 0 ) const; + + /*! @method getResources + @abstract Overridden to call the IOPlatformExpert getNubResources method + @discussion + I have no idea why this is done, but the Apple code did it, so this + code does too. + */ + virtual IOReturn getResources( void ); + + /*! @method message + @abstract Overridden to receive ACPI notifications + @discussion + Allows the notifications to be forwarded to the keyboard device such that + ACPI Notify can be used to push keystrokes. This is used to convert ACPI + keys such that they appear to be PS2 keys. + */ + virtual IOReturn message( UInt32 type, IOService* provider, void* argument ); +}; + +#endif \ No newline at end of file diff --git a/VoodooPS2Controller/ApplePS2Device.cpp b/VoodooPS2Controller/ApplePS2Device.cpp new file mode 100644 index 0000000..cbba7da --- /dev/null +++ b/VoodooPS2Controller/ApplePS2Device.cpp @@ -0,0 +1,155 @@ +/* + * Copyright (c) 1998-2000 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * The contents of this file constitute Original Code as defined in and + * are subject to the Apple Public Source License Version 1.1 (the + * "License"). You may not use this file except in compliance with the + * License. Please obtain a copy of the License at + * http://www.apple.com/publicsource and read it before using this file. + * + * This Original Code and all software distributed under the License are + * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the + * License for the specific language governing rights and limitations + * under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +#include "ApplePS2Device.h" +#include "VoodooPS2Controller.h" + +OSDefineMetaClassAndStructors(ApplePS2Device, IOService); + +// ============================================================================= +// ApplePS2Device Class Implementation +// + +bool ApplePS2Device::attach(IOService * provider) +{ + if (!super::attach(provider)) + return false; + + assert(_controller == 0); + _controller = (ApplePS2Controller*)provider; + _controller->retain(); + + return true; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +void ApplePS2Device::detach( IOService * provider ) +{ + assert(_controller == provider); + _controller->release(); + _controller = 0; + + super::detach(provider); +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +PS2Request * ApplePS2Device::allocateRequest(int max) +{ + return _controller->allocateRequest(max); +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +void ApplePS2Device::freeRequest(PS2Request * request) +{ + _controller->freeRequest(request); +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +bool ApplePS2Device::submitRequest(PS2Request * request) +{ + return _controller->submitRequest(request); +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +void ApplePS2Device::submitRequestAndBlock(PS2Request * request) +{ + _controller->submitRequestAndBlock(request); +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +UInt8 ApplePS2Device::setCommandByte(UInt8 setBits, UInt8 clearBits) +{ + return _controller->setCommandByte(setBits, clearBits); +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +void ApplePS2Device::lock() +{ + _controller->lock(); +} + +void ApplePS2Device::unlock() +{ + _controller->unlock(); +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +void ApplePS2Device::installInterruptAction(OSObject * target, + PS2InterruptAction interruptAction, + PS2PacketAction packetAction) +{ + _controller->installInterruptAction(_deviceType, target, interruptAction, packetAction); +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +void ApplePS2Device::uninstallInterruptAction() +{ + _controller->uninstallInterruptAction(_deviceType); +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +void ApplePS2Device::installPowerControlAction( + OSObject * target, + PS2PowerControlAction action) +{ + _controller->installPowerControlAction(_deviceType, target, action); +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +void ApplePS2Device::uninstallPowerControlAction() +{ + _controller->uninstallPowerControlAction(_deviceType); +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +void ApplePS2Device::installMessageAction(OSObject* target, PS2MessageAction action) +{ + _controller->installMessageAction(_deviceType, target, action); +} + +void ApplePS2Device::uninstallMessageAction() +{ + _controller->uninstallMessageAction(_deviceType); +} + +void ApplePS2Device::dispatchMouseMessage(int message, void *data) +{ + _controller->dispatchMessage(kDT_Mouse, message, data); +} + +void ApplePS2Device::dispatchKeyboardMessage(int message, void *data) +{ + _controller->dispatchMessage(kDT_Keyboard, message, data); +} + diff --git a/VoodooPS2Controller/ApplePS2Device.h b/VoodooPS2Controller/ApplePS2Device.h new file mode 100644 index 0000000..21077c3 --- /dev/null +++ b/VoodooPS2Controller/ApplePS2Device.h @@ -0,0 +1,634 @@ +/* + * Copyright (c) 1998-2000 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * The contents of this file constitute Original Code as defined in and + * are subject to the Apple Public Source License Version 1.1 (the + * "License"). You may not use this file except in compliance with the + * License. Please obtain a copy of the License at + * http://www.apple.com/publicsource and read it before using this file. + * + * This Original Code and all software distributed under the License are + * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the + * License for the specific language governing rights and limitations + * under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +#ifndef _APPLEPS2DEVICE_H +#define _APPLEPS2DEVICE_H + +#include +#include +#include +#include +#include + +#ifdef DEBUG_MSG +#define DEBUG_LOG(args...) do { IOLog(args); } while (0) +#else +#define DEBUG_LOG(args...) do { } while (0) +#endif + +#define countof(x) (sizeof((x))/sizeof((x)[0])) + +#define EXPORT __attribute__((visibility("default"))) + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +// Definitions +// +// Data Port (0x60) Commands. These commands are all transmitted directly to +// the physical keyboard and/or mouse, so expect an acknowledge for each byte +// that you send through this port. +// + +#define kDP_SetMouseScaling1To1 0xE6 // (mouse) +#define kDP_SetMouseScaling2To1 0xE7 // (mouse) +#define kDP_SetMouseResolution 0xE8 // (mouse) +#define kDP_GetMouseInformation 0xE9 // (mouse) +#define kDP_SetMouseStreamMode 0xEA // (mouse) +#define kDP_MousePoll 0xEB // (mouse) caller sets number of bytes to receive +#define kDP_MouseResetWrap 0xEC // (mouse) +#define kDP_SetKeyboardLEDs 0xED // (keyboard) +#define kDP_TestKeyboardEcho 0xEE // (keyboard) +#define kDP_GetSetKeyboardASCs 0xF0 // (keyboard) +#define kDP_MouseSetPoll 0xF0 // (mouse) +#define kDP_GetId 0xF2 // (keyboard+mouse) +#define kDP_SetKeyboardTypematic 0xF3 // (keyboard) +#define kDP_SetMouseSampleRate 0xF3 // (mouse) +#define kDP_Enable 0xF4 // (keyboard+mouse) +#define kDP_SetDefaultsAndDisable 0xF5 // (keyboard+mouse) +#define kDP_SetDefaults 0xF6 // (keyboard+mouse) +#define kDP_SetAllTypematic 0xF7 // (keyboard) +#define kDP_SetAllMakeRelease 0xF8 // (keyboard) +#define kDP_SetAllMakeOnly 0xF9 // (keyboard) +#define kDP_SetAllTypematicMakeRelease 0xFA // (keyboard) +#define kDP_SetKeyMakeRelease 0xFB // (keyboard) +#define kDP_SetKeyMakeOnly 0xFC // (keyboard) +#define kDP_Reset 0xFF // (keyboard+mouse) + +// +// Command Port (0x64) Commands. These commands all access registers local +// to the motherboard, ie. nothing is transmitted, thus these commands and +// any associated data passed thru the Data Port do not return acknowledges. +// + +#define kCP_GetCommandByte 0x20 // (keyboard+mouse) +#define kCP_ReadControllerRAMBase 0x21 // +#define kCP_SetCommandByte 0x60 // (keyboard+mouse) +#define kCP_WriteControllerRAMBase 0x61 // +#define kCP_TestPassword 0xA4 // +#define kCP_GetPassword 0xA5 // +#define kCP_VerifyPassword 0xA6 // +#define kCP_DisableMouseClock 0xA7 // (mouse) +#define kCP_EnableMouseClock 0xA8 // (mouse) +#define kCP_TestMousePort 0xA9 // +#define kCP_TestController 0xAA // +#define kCP_TestKeyboardPort 0xAB // +#define kCP_GetControllerDiagnostic 0xAC // +#define kCP_DisableKeyboardClock 0xAD // (keyboard) +#define kCP_EnableKeyboardClock 0xAE // (keyboard) +#define kCP_ReadInputPort 0xC0 // +#define kCP_PollInputPortLow 0xC1 // +#define kCP_PollInputPortHigh 0xC2 // +#define kCP_ReadOutputPort 0xD0 // +#define kCP_WriteOutputPort 0xD1 // +#define kCP_WriteKeyboardOutputBuffer 0xD2 // (keyboard) +#define kCP_WriteMouseOutputBuffer 0xD3 // (mouse) +#define kCP_TransmitToMouse 0xD4 // (mouse) +#define kCP_ReadTestInputs 0xE0 // +#define kCP_PulseOutputBitBase 0xF0 // + +// +// Bit definitions for the 8-bit "Command Byte" register, which is accessed +// through the kCP_GetCommandByte/kCP_SetCommandByte. kCP_DisableMouseClock, +// kCP_EnableMouseClock, kCP_DisableKeyboardClock, and kCP_EnableKeyboardClock +// also affect this register. +// + +#define kCB_EnableKeyboardIRQ 0x01 // Enable Keyboard IRQ +#define kCB_EnableMouseIRQ 0x02 // Enable Mouse IRQ +#define kCB_SystemFlag 0x04 // Set System Flag +#define kCB_InhibitOverride 0x08 // Inhibit Override +#define kCB_DisableKeyboardClock 0x10 // Disable Keyboard Clock +#define kCB_DisableMouseClock 0x20 // Disable Mouse Clock +#define kCB_TranslateMode 0x40 // Keyboard Translate Mode + +// +// Bit definitions for the 8-bit "LED" register, which is accessed through +// the Data Port (0x60) via kDP_SetKeyboardLEDs. Undefined bit positions must be zero. +// + +#define kLED_ScrollLock 0x01 // Scroll Lock +#define kLED_NumLock 0x02 // Num Lock +#define kLED_CapsLock 0x04 // Caps Lock + +// +// Scan Codes used for special purposes on the keyboard and/or mouse receive +// port. These values would be received from your interrupt handler or from +// a ReadDataPort command primitive. These values do not represent actual +// keys, but indicate some sort of status. +// + +#define kSC_Acknowledge 0xFA // ack for transmitted commands +#define kSC_Extend 0xE0 // marker for "extended" sequence +#define kSC_Pause 0xE1 // marker for pause key sequence +#define kSC_Resend 0xFE // request to resend keybd cmd +#define kSC_Reset 0xAA // the keyboard/mouse has reset +#define kSC_UpBit 0x80 // OR'd in if key below is released +#define kSC_ID 0x00 // PSMOUSE_RET_ID + +// +// Scan Codes for some modifier keys. +// + +#define kSC_Alt 0x38 // (extended = right key) +#define kSC_Ctrl 0x1D // (extended = right key) +#define kSC_ShiftLeft 0x2A +#define kSC_ShiftRight 0x36 +#define kSC_WindowsLeft 0x5B // extended +#define kSC_WindowsRight 0x5C // extended + +// +// Scan Codes for some keys. +// + +#define kSC_Delete 0x53 // (extended = gray key) +#define kSC_NumLock 0x45 + +// name of drivers/services as registered + +#define kApplePS2Controller "ApplePS2Controller" +#define kApplePS2Keyboard "ApplePS2Keyboard" + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +// RingBuffer +// +// A simple ring buffer class for devices to use in their real interrupt +// routine for buffering packets. +// +// Standard FIFO ring buffer implemented as an array. +// +// Note: there is no error checking in this class. And no check for +// overflow/underflow conditions. Make sure you have a large enough +// buffer to handle your needs. And don't advance or try to fetch +// data that doesn't exist (need to check result from count() first) +// +// This class is written for simplicity and efficiency, not to be +// user friendly. +// +// The tail and head buffer can be accessed directly for effeciency, +// but there are no provisions for dealing with "wrap-around," so it +// is best that your buffer size is a mutliple of the packet size. +// + +template +class RingBuffer +{ +private: + T m_buffer[N]; + volatile unsigned m_head; // m_head is volatile: commonly accessed at interrupt time + unsigned m_tail; + unsigned count(unsigned head, unsigned tail) + { + if (head >= tail) + return head - tail; + else + return N - tail + head; + } + +public: + inline RingBuffer() { reset(); } + void reset() + { + m_head = 0; + m_tail = 0; + } + inline unsigned count() { return count(m_head, m_tail); } + void push(T data) + { + // add new data to head, check for overflow. + unsigned new_head = m_head + 1; + if (new_head >= N) + new_head = 0; + if (new_head != m_tail) + { + m_buffer[m_head] = data; + m_head = new_head; + } + } + T fetch() + { + // grab new data from tail, no check for underflow. + T result = m_buffer[m_tail++]; + if (m_tail >= N) + m_tail = 0; + return result; + } + inline T* head() { return &m_buffer[m_head]; } + inline T* tail() { return &m_buffer[m_tail]; } + void advanceHead(unsigned move) + { + // advance head by specified amount, check for overflow + unsigned new_head = m_head + move; + if (new_head >= N) + new_head -= N; + if (count(new_head, m_tail) >= count()) + m_head = new_head; + } + void advanceTail(unsigned move) + { + // advance tail by specified amount, no check for underflow. + m_tail += move; + if (m_tail >= N) + m_tail -= N; + } +}; + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +// PS/2 Command Primitives +// +// o kPS2C_ReadDataPort: +// o Description: Reads the next available byte off the data port (60h). +// o Out Field: Holds byte that was read. +// +// o kPS2C_ReadDataAndCompare: +// o Description: Reads the next available byte off the data port (60h), +// and compares it with the byte in the In Field. If the +// comparison fails, the request is aborted (refer to the +// commandsCount field in the request structure). +// o In Field: Holds byte that comparison should be made to. +// +// o kPS2C_WriteDataPort: +// o Description: Writes the byte in the In Field to the data port (60h). +// o In Field: Holds byte that should be written. +// +// o kPS2C_WriteCommandPort: +// o Description: Writes the byte in the In Field to the command port (64h). +// o In Field: Holds byte that should be written. +// + +enum PS2CommandEnum +{ + kPS2C_ReadDataPort, + kPS2C_ReadDataPortAndCompare, + kPS2C_WriteDataPort, + kPS2C_WriteCommandPort, + kPS2C_SendMouseCommandAndCompareAck, + kPS2C_ReadMouseDataPort, + kPS2C_ReadMouseDataPortAndCompare, + kPS2C_FlushDataPort, + kPS2C_SleepMS, + kPS2C_ModifyCommandByte, +}; +typedef enum PS2CommandEnum PS2CommandEnum; + +struct PS2Command +{ + PS2CommandEnum command; + union + { + UInt8 inOrOut; + UInt32 inOrOut32; + struct + { + UInt8 setBits; + UInt8 clearBits; + UInt8 oldBits; + }; + }; +}; +typedef struct PS2Command PS2Command; + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +// PS/2 Request Structure +// +// o General Notes: +// o allocateRequest allocates the request structure -- use it always. +// o freeRequest deallocates the request structure -- use it always. +// o It is the driver's responsibility to free the request structure: +// o after a submitRequestAndBlock call returns, or +// o in the completion routine for each submitRequest issued. +// o It is not the driver's resposiblity to free the request structure: +// o when no completion routine is specified in a request issued via +// submitRequest, in which case the request is freed automatically +// by the controller. This case is called "fire-and-forget". +// o On completion, the requester can see how far the processing got by +// looking at the commandsCount field. If it is equal to the original +// number of commands, then the request was successful. If isn't, the +// value represents the zero-based index of the command that failed. +// +// o General Notes For Inquisitive Minds: +// o Requests are executed atomically with respect to all other requests, +// that is, if a keyboard request is currently being processed, then a +// request submitted by the mouse driver or one submitted by a separate +// thread of control in the keyboard driver will get queued until the +// controller is available again. +// o Request processing can be preempted to service interrupts on other +// PS/2 devices, should other-device data arrive unexpectedly on the +// input stream while processing a request. +// o The request processor knows when to read the mouse input stream +// over the keyboard input stream for a given command sequence. It +// does not depend on which driver it came from, rest assurred. If +// the mouse driver so chose, it could send keyboard commands. +// +// o commands: +// o Description: Holds list of commands that controller should execute. +// o Comments: Refer to PS2Command structure. +// +// o commandsCount: +// o Description: Holds the number of commands in the command list. +// o Comments: Number of commands should never exceed kMaxCommands. +// +// o completionRoutineTarget, Action, and Param: +// o Description: Object and method of the completion routine, which is +// called when the request has finished. The Param field +// may be filled with anything you want; it is passed to +// completion routine when it is called. These fields +// are optional. If left null, the request structure +// will be deallocated automatically by the controller +// on completion of the request. +// o Prototype: void completionRoutine(void * target, void * param); +// o Comments: Never issue submitRequestAndBlock or otherwise BLOCK on +// any request sent down to your device from the completion +// routine. Obey, or deadlock. +// +// Extensions for allocation by RehabMan +// +// Here are the possible ways to allocate/free the PS2Request structure. +// All examples assume _device is pointer to ApplePS2MouseDevice or +// ApplePS2KeyboardDevice. +// +// Legacy (allocation): +// PS2Request* request = _device->allocateRequest(); +// //... fill in request and submit +// _device->freeRequest(request); +// // Note: no need to free if using completionRoutine or on +// // async submits. +// +// New way (allocation): +// // specify number of commands +// PS2Request* request = _device->allocateRequest(12); +// +// // allocate on stack +// TPS2Request<12> request; +// // Note: For obvious reasons, only valid with submitRequestAndBlock +// +// // allocate on stack using default size +// TPS2Request<> request; +// +// // not allowed +// PS2Request request; // no public constructor +// +// // allowed, but probably not a good idea: +// TPS2Request request<0>; // equivalent to above +// PS2Request* request = _device->allocateRequest(0); +// +// Deallocation: +// _device->freeRequest(request); +// +// For stack-based allocation, and blocking submit do not +// freeRequest or delete. +// + +#define kMaxCommands 30 + +typedef void (*PS2CompletionAction)(void * target, void * param); + +struct PS2Request +{ + friend class ApplePS2Controller; + +protected: + PS2Request(); + static void* operator new(size_t); // "hide" it + static inline void* operator new(size_t, int max) + { return ::operator new(sizeof(PS2Request) + sizeof(PS2Command)*max); } + static inline void operator delete(void*p) + { ::operator delete(p); } + +public: + UInt8 commandsCount; + void * completionTarget; + PS2CompletionAction completionAction; + void * completionParam; + queue_chain_t chain; + PS2Command commands[0]; +}; + +//template struct TPS2Request : public PS2Request +// special completionTarget for TPS2Request allocated on stack +#define kStackCompletionTarget ((void*)1) + +// PS2Requests with a completion target: completionAction frees the memory +// PS2Requests allocated on the stack: completionTarget must be kStackCompletionTarget +// PS2Requests with zero completionTarget: automatically freed upon completion + +// base class for templated PS2Request on the stack +struct PS2RequestStack : public PS2Request +{ +protected: + PS2RequestStack() { completionTarget = kStackCompletionTarget; } +}; + +template struct TPS2Request : public PS2RequestStack +{ +public: + PS2Command commands[max]; +}; + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +// ApplePS2KeyboardDevice and ApplePS2MouseDevice Class Descriptions +// +// +// o General Notes: +// o When the probe method is invoked on the client driver, the controller +// guarantees that the keyboard clock is enabled and the keyboard itself +// is disabled. This implies the client driver can send commands to the +// keyboard without a problem, and the keyboard itself will not send any +// asynchronous key data that may mess up the responses expected by the +// commands sent to it. +// +// o installInterruptAction: +// o Description: Ask the device to deliver asynchronous data to driver. +// o In Fields: Target/action of completion routine. +// +// o installInterruptAction Interrupt Routine: +// o Description: Delivers a newly read byte from the input data stream. +// o Prototype: void interruptOccurred(void * target, UInt8 byte); +// o In Fields: Byte that was read. +// o Comments: Never issue submitRequestAndBlock or otherwise BLOCK on +// any request sent down to your device from the interrupt +// routine. Obey, or deadlock. +// +// o uninstallInterruptHandler: +// o Description: Ask the device to stop delivering asynchronous data. +// +// o allocateRequest: +// o Description: Allocate a request structure, blocks until successful. +// o Result: Request structure pointer. +// o Comments: Request structure is guaranteed to be zeroed. +// +// o freeRequest: +// o Description: Deallocate a request structure. +// o In Fields: Request structure pointer. +// +// o submitRequest: +// o Description: Submit the request to the controller for processing. +// o In Fields: Request structure pointer. +// o Result: kern_return_t queueing status. +// +// o submitRequestAndBlock: +// o Description: Submit the request to the controller for processing, then +// block the calling thread until the request completes. +// o In Fields: Request structure pointer. +// + +enum PS2InterruptResult +{ + kPS2IR_packetReady, + kPS2IR_packetBuffering, +}; + +typedef PS2InterruptResult (*PS2InterruptAction)(void * target, UInt8 data); + +typedef void (*PS2PacketAction)(void * target); + +// +// Defines the prototype of an action registered by a PS/2 device driver to +// intercept power changes on the PS/2 controller, and to manage the device +// accordingly. +// + +typedef void (*PS2PowerControlAction)(void * target, UInt32 whatToDo); + +// +// Defines the prototype of an action registered by a PS/2 device driver +// to communicate with either the mouse or keyboard partner. +// +// This is a extensible mechanism for keyboard driver to talk to +// mouse/trackpad driver... or for mouse/trackpad driver to talk to +// the keyboard driver. +// + +typedef void (*PS2MessageAction)(void* target, int message, void* data); + +enum +{ + // from keyboard to mouse/touchpad + kPS2M_setDisableTouchpad, // set disable/enable touchpad (data is bool*) + kPS2M_getDisableTouchpad, // get disable/enable touchpad (data is bool*) + kPS2M_notifyKeyPressed, // notify of time key pressed (data is PS2KeyInfo*) + + // from mouse/touchpad to keyboard + kPS2M_swipeDown, + kPS2M_swipeUp, + kPS2M_swipeLeft, + kPS2M_swipeRight, + kPS2M_swipe4Down, + kPS2M_swipe4Up, + kPS2M_swipe4Left, + kPS2M_swipe4Right, +}; + +typedef struct PS2KeyInfo +{ + int64_t time; + UInt16 adbKeyCode; + bool goingDown; + bool eatKey; +} PS2KeyInfo; + + +// +// Enumeration of 'whatToDo' values passed to power control action. +// + +enum +{ + kPS2C_DisableDevice, + kPS2C_EnableDevice +}; + +// PS/2 device types. + +typedef enum +{ + kDT_Keyboard, + kDT_Mouse, +#if WATCHDOG_TIMER + kDT_Watchdog, +#endif +} PS2DeviceType; + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +// ApplePS2Device Class Declaration +// + +class ApplePS2Controller; + +class EXPORT ApplePS2Device : public IOService +{ + typedef IOService super; + OSDeclareDefaultStructors(ApplePS2Device); + +protected: + ApplePS2Controller* _controller; + PS2DeviceType _deviceType; + +public: + virtual bool attach(IOService * provider); + virtual void detach(IOService * provider); + + // Interrupt Handling Routines + + virtual void installInterruptAction(OSObject *, PS2InterruptAction, PS2PacketAction); + virtual void uninstallInterruptAction(); + + // Request Submission Routines + + virtual PS2Request* allocateRequest(int max = kMaxCommands); + virtual void freeRequest(PS2Request * request); + virtual bool submitRequest(PS2Request * request); + virtual void submitRequestAndBlock(PS2Request * request); + virtual UInt8 setCommandByte(UInt8 setBits, UInt8 clearBits); + + // Power Control Handling Routines + + virtual void installPowerControlAction(OSObject *, PS2PowerControlAction); + virtual void uninstallPowerControlAction(); + + // Messaging + + virtual void installMessageAction(OSObject*, PS2MessageAction); + virtual void uninstallMessageAction(); + virtual void dispatchMouseMessage(int message, void *data); + virtual void dispatchKeyboardMessage(int message, void *data); + + // Exclusive access (command byte contention) + + virtual void lock(); + virtual void unlock(); +}; + +#if 0 // Note: Now using architecture/i386/pio.h (see above) +typedef unsigned short i386_ioport_t; +inline unsigned char inb(i386_ioport_t port) +{ + unsigned char datum; + asm volatile("inb %1, %0" : "=a" (datum) : "d" (port)); + return(datum); +} + +inline void outb(i386_ioport_t port, unsigned char datum) +{ + asm volatile("outb %0, %1" : : "a" (datum), "d" (port)); +} +#endif + +#endif /* !_APPLEPS2DEVICE_H */ diff --git a/VoodooPS2Controller/ApplePS2KeyboardDevice.cpp b/VoodooPS2Controller/ApplePS2KeyboardDevice.cpp new file mode 100644 index 0000000..6ea687b --- /dev/null +++ b/VoodooPS2Controller/ApplePS2KeyboardDevice.cpp @@ -0,0 +1,40 @@ +/* + * Copyright (c) 1998-2000 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * The contents of this file constitute Original Code as defined in and + * are subject to the Apple Public Source License Version 1.1 (the + * "License"). You may not use this file except in compliance with the + * License. Please obtain a copy of the License at + * http://www.apple.com/publicsource and read it before using this file. + * + * This Original Code and all software distributed under the License are + * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the + * License for the specific language governing rights and limitations + * under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +#include "ApplePS2KeyboardDevice.h" +#include "VoodooPS2Controller.h" + +// ============================================================================= +// ApplePS2KeyboardDevice Class Implementation +// + +OSDefineMetaClassAndStructors(ApplePS2KeyboardDevice, ApplePS2Device); + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +bool ApplePS2KeyboardDevice::init() +{ + bool result = super::init(); + _deviceType = kDT_Keyboard; + return result; +} + diff --git a/VoodooPS2Controller/ApplePS2KeyboardDevice.h b/VoodooPS2Controller/ApplePS2KeyboardDevice.h new file mode 100644 index 0000000..30b7cb5 --- /dev/null +++ b/VoodooPS2Controller/ApplePS2KeyboardDevice.h @@ -0,0 +1,39 @@ +/* + * Copyright (c) 1998-2000 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * The contents of this file constitute Original Code as defined in and + * are subject to the Apple Public Source License Version 1.1 (the + * "License"). You may not use this file except in compliance with the + * License. Please obtain a copy of the License at + * http://www.apple.com/publicsource and read it before using this file. + * + * This Original Code and all software distributed under the License are + * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the + * License for the specific language governing rights and limitations + * under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +#ifndef _APPLEPS2KEYBOARDDEVICE_H +#define _APPLEPS2KEYBOARDDEVICE_H + +#include "ApplePS2Device.h" + +class ApplePS2Controller; + +class EXPORT ApplePS2KeyboardDevice : public ApplePS2Device +{ + typedef ApplePS2Device super; + OSDeclareDefaultStructors(ApplePS2KeyboardDevice); + +public: + virtual bool init(); +}; + +#endif /* !_APPLEPS2KEYBOARDDEVICE_H */ diff --git a/VoodooPS2Controller/ApplePS2MouseDevice.cpp b/VoodooPS2Controller/ApplePS2MouseDevice.cpp new file mode 100644 index 0000000..b9fb5f2 --- /dev/null +++ b/VoodooPS2Controller/ApplePS2MouseDevice.cpp @@ -0,0 +1,40 @@ +/* + * Copyright (c) 1998-2000 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * The contents of this file constitute Original Code as defined in and + * are subject to the Apple Public Source License Version 1.1 (the + * "License"). You may not use this file except in compliance with the + * License. Please obtain a copy of the License at + * http://www.apple.com/publicsource and read it before using this file. + * + * This Original Code and all software distributed under the License are + * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the + * License for the specific language governing rights and limitations + * under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +#include "ApplePS2MouseDevice.h" +#include "VoodooPS2Controller.h" + +// ============================================================================= +// ApplePS2MouseDevice Class Implementation +// + +OSDefineMetaClassAndStructors(ApplePS2MouseDevice, ApplePS2Device); + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +bool ApplePS2MouseDevice::init() +{ + bool result = super::init(); + _deviceType = kDT_Mouse; + return result; +} + diff --git a/VoodooPS2Controller/ApplePS2MouseDevice.h b/VoodooPS2Controller/ApplePS2MouseDevice.h new file mode 100644 index 0000000..3a88538 --- /dev/null +++ b/VoodooPS2Controller/ApplePS2MouseDevice.h @@ -0,0 +1,39 @@ +/* + * Copyright (c) 1998-2000 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * The contents of this file constitute Original Code as defined in and + * are subject to the Apple Public Source License Version 1.1 (the + * "License"). You may not use this file except in compliance with the + * License. Please obtain a copy of the License at + * http://www.apple.com/publicsource and read it before using this file. + * + * This Original Code and all software distributed under the License are + * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the + * License for the specific language governing rights and limitations + * under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +#ifndef _APPLEPS2MOUSEDEVICE_H +#define _APPLEPS2MOUSEDEVICE_H + +#include "ApplePS2Device.h" + +class ApplePS2Controller; + +class EXPORT ApplePS2MouseDevice : public ApplePS2Device +{ + typedef ApplePS2Device super; + OSDeclareDefaultStructors(ApplePS2MouseDevice); + +public: + virtual bool init(); +}; + +#endif /* !_APPLEPS2MOUSEDEVICE_H */ diff --git a/VoodooPS2Controller/VoodooPS2Controller-Info.plist b/VoodooPS2Controller/VoodooPS2Controller-Info.plist new file mode 100644 index 0000000..1ac57e3 --- /dev/null +++ b/VoodooPS2Controller/VoodooPS2Controller-Info.plist @@ -0,0 +1,144 @@ + + + + + CFBundleExecutable + VoodooPS2Controller + CFBundleGetInfoString + ${MODULE_VERSION}, Copyright Apple Computer, Inc. 2000-2003, David Elliot 2007, RehabMan 2012-2013 + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + Voodoo PS/2 Controller + CFBundlePackageType + KEXT + CFBundleShortVersionString + ${MODULE_VERSION} + CFBundleSignature + ???? + CFBundleVersion + ${MODULE_VERSION} + IOKitPersonalities + + ACPI PS/2 Nub + + CFBundleIdentifier + org.rehabman.voodoo.driver.PS2Controller + IOClass + AppleACPIPS2Nub + IONameMatch + + PNP0303 + PNP030B + + IOProviderClass + IOACPIPlatformDevice + MouseNameMatch + + PNP0F03 + PNP0F0B + PNP0F0E + PNP0F13 + + + ApplePS2Controller + + CFBundleIdentifier + org.rehabman.voodoo.driver.PS2Controller + IOClass + ApplePS2Controller + IONameMatch + ps2controller + IOProviderClass + IOPlatformDevice + Platform Profile + + Default + + WakeDelay + 10 + MouseWakeFirst + + + HPQOEM + + 1411 + ProBook + 1619 + ProBook + 161C + ProBook + 164F + ProBook + 167C + ProBook + 167E + ProBook + 1680 + ProBook + 179B + ProBook + 179C + ProBook + 17A9 + ProBook + 17F0 + ProBook + 17F3 + ProBook + 17F6 + ProBook + 1942 + ProBook + 1949 + ProBook + 198F + ProBook + ProBook + + WakeDelay + 0 + + + Lenovo + + Haswell-Ideapad + + WakeDelay + 0 + + U430 + Haswell-Ideapad + + + RM,Build + ${CONFIGURATION}-${LOGNAME} + RM,Version + ${PRODUCT_NAME} ${MODULE_VERSION} + + + OSBundleCompatibleVersion + 2.8.15 + OSBundleLibraries + + com.apple.iokit.IOACPIFamily + 1.0.0d1 + com.apple.kpi.bsd + 8.0.0 + com.apple.kpi.iokit + 8.0.0 + com.apple.kpi.libkern + 8.0.0 + com.apple.kpi.mach + 8.0.0 + com.apple.kpi.unsupported + 8.0.0 + + OSBundleRequired + Console + Source Code + https://github.com/RehabMan/OS-X-Voodoo-PS2-Controller + + diff --git a/VoodooPS2Controller/VoodooPS2Controller-Prefix.pch b/VoodooPS2Controller/VoodooPS2Controller-Prefix.pch new file mode 100644 index 0000000..e2df1c6 --- /dev/null +++ b/VoodooPS2Controller/VoodooPS2Controller-Prefix.pch @@ -0,0 +1,4 @@ +// +// Prefix header for all source files of the 'VoodooPS2Controller' target in the 'VoodooPS2Controller' project +// + diff --git a/VoodooPS2Controller/VoodooPS2Controller.cpp b/VoodooPS2Controller/VoodooPS2Controller.cpp new file mode 100644 index 0000000..858df41 --- /dev/null +++ b/VoodooPS2Controller/VoodooPS2Controller.cpp @@ -0,0 +1,2233 @@ +/* + * Copyright (c) 1998-2000 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * The contents of this file constitute Original Code as defined in and + * are subject to the Apple Public Source License Version 1.1 (the + * "License"). You may not use this file except in compliance with the + * License. Please obtain a copy of the License at + * http://www.apple.com/publicsource and read it before using this file. + * + * This Original Code and all software distributed under the License are + * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the + * License for the specific language governing rights and limitations + * under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +#define DISABLE_CLOCKS_IRQS_BEFORE_SLEEP 1 +#define FULL_INIT_AFTER_WAKE 1 + +#include +#include +#include +#include +#include "ApplePS2KeyboardDevice.h" +#include "ApplePS2MouseDevice.h" +#include "VoodooPS2Controller.h" + +//REVIEW: avoids problem with Xcode 5.1.0 where -dead_strip eliminates these required symbols +#include +void* _org_rehabman_dontstrip_[] = +{ + (void*)&OSKextGetCurrentIdentifier, + (void*)&OSKextGetCurrentLoadTag, + (void*)&OSKextGetCurrentVersionString, +}; + +enum { + kPS2PowerStateSleep = 0, + kPS2PowerStateDoze = 1, + kPS2PowerStateNormal = 2, + kPS2PowerStateCount +}; + +static const IOPMPowerState PS2PowerStateArray[ kPS2PowerStateCount ] = +{ + { 1,0,0,0,0,0,0,0,0,0,0,0 }, + { 1,kIOPMDeviceUsable, kIOPMDoze, kIOPMDoze, 0,0,0,0,0,0,0,0 }, + { 1,kIOPMDeviceUsable, IOPMPowerOn, IOPMPowerOn, 0,0,0,0,0,0,0,0 } +}; + +// ============================================================================= +// Interrupt-Time Support Functions +// + +//static +void ApplePS2Controller::interruptHandlerMouse(OSObject*, void* refCon, IOService*, int) +{ + ApplePS2Controller* me = (ApplePS2Controller*)refCon; + if (me->_ignoreInterrupts) + return; + + // + // Wake our workloop to service the interrupt. This is an edge-triggered + // interrupt, so returning from this routine without clearing the interrupt + // condition is perfectly normal. + // +#if HANDLE_INTERRUPT_DATA_LATER + me->_interruptSourceMouse->interruptOccurred(0, 0, 0); +#else + me->handleInterrupt(kDT_Mouse); +#endif +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +//static +void ApplePS2Controller::interruptHandlerKeyboard(OSObject*, void* refCon, IOService*, int) +{ + ApplePS2Controller* me = (ApplePS2Controller*)refCon; + if (me->_ignoreInterrupts) + return; + +#if DEBUGGER_SUPPORT + // + // The keyboard interrupt handler reads in the pending scan code and stores + // it on our internal queue; should it completes a debugger escape sequence, + // we jump to the debugger function immediately. + // + + UInt8 key; + UInt8 status; + int state; + + // Lock out the keyboard interrupt handler [redundant here] and claim + // exclusive access to the internal keyboard queue. + + me->lockController(&state); + + // Verify that data is available on the controller's input port. + + if ( ((status = inb(kCommandPort)) & kOutputReady) ) + { + // Verify that the data is keyboard data, otherwise call mouse handler. + // This case should never really happen, but if it does, we handle it. + + if ( (status & kMouseData) ) + { + interruptHandlerMouse(0, refCon, 0, 0); + } + else + { + // Retrieve the keyboard data on the controller's input port. + + IODelay(kDataDelay); + key = inb(kDataPort); + + // Call the debugger-key-sequence checking code (if a debugger sequence + // completes, the debugger function will be invoked immediately within + // doEscape). The doEscape call may insist that we drop the scan code + // we just received in some cases (a true return) -- we don't question + // it's judgement and comply. No escape check if debugging is disabled. + + if (me->_debuggingEnabled == false || me->doEscape(key) == false) + me->enqueueKeyboardData(key); + + // In all cases, we wake up our workloop to service the interrupt data. + me->_interruptSourceKeyboard->interruptOccurred(0, 0, 0); + } + } + + // Remove the lockout on the keyboard interrupt handler [ineffective here] + // and release our exclusive access to the internal keyboard queue. + + me->unlockController(state); +#else + // + // Wake our workloop to service the interrupt. This is an edge-triggered + // interrupt, so returning from this routine without clearing the interrupt + // condition is perfectly normal. + // +#if HANDLE_INTERRUPT_DATA_LATER + me->_interruptSourceKeyboard->interruptOccurred(0, 0, 0); +#else + me->handleInterrupt(kDT_Keyboard); +#endif + +#endif //DEBUGGER_SUPPORT +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +#if WATCHDOG_TIMER + +void ApplePS2Controller::onWatchdogTimer() +{ + if (!_ignoreInterrupts) + handleInterrupt(kDT_Watchdog); + _watchdogTimer->setTimeoutMS(kWatchdogTimerInterval); +} + +#endif // WATCHDOG_TIMER + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +#if !HANDLE_INTERRUPT_DATA_LATER + +void ApplePS2Controller::handleInterrupt(PS2DeviceType deviceType) +{ + ////IOLog("%s:handleInterrupt(%s)\n", getName(), deviceType == kDT_Keyboard ? "kDT_Keyboard" : deviceType == kDT_Watchdog ? "kDT_Watchdog" : "kDT_Mouse"); + + // Loop only while there is data currently on the input stream. + + bool wakeMouse = false; + bool wakeKeyboard = false; + while (1) + { + // while getting status and reading the port, no interrupts... + bool enable = ml_set_interrupts_enabled(false); + IODelay(kDataDelay); + UInt8 status = inb(kCommandPort); + if (!(status & kOutputReady)) + { + // no data available, so break out and return + ml_set_interrupts_enabled(enable); + break; + } + +#if WATCHDOG_TIMER + // do not process mouse data in watchdog timer + if (deviceType == kDT_Watchdog && (status & kMouseData)) + { + ml_set_interrupts_enabled(enable); + break; + } +#endif + + // read the data + IODelay(kDataDelay); + UInt8 data = inb(kDataPort); + + // now ok for interrupts, we have read status, and found data... + // (it does not matter [too much] if keyboard data is delivered out of order) + ml_set_interrupts_enabled(enable); + +#if WATCHDOG_TIMER + //REVIEW: remove this debug eventually... + if (deviceType == kDT_Watchdog) + IOLog("%s:handleInterrupt(kDT_Watchdog): %s = %02x\n", getName(), status & kMouseData ? "mouse" : "keyboard", data); +#endif + if (status & kMouseData) + { + // Dispatch the data to the mouse driver. + if (kPS2IR_packetReady == _dispatchDriverInterrupt(kDT_Mouse, data)) + wakeMouse = true; + } + else + { + // Dispatch the data to the keyboard driver. + if (kPS2IR_packetReady == _dispatchDriverInterrupt(kDT_Keyboard, data)) + wakeKeyboard = true; + } + } // while (forever) + + // wake up workloop based mouse interrupt source if needed + if (wakeMouse) + _interruptSourceMouse->interruptOccurred(0, 0, 0); + // wake up workloop based keyboard interrupt source if needed + if (wakeKeyboard) + _interruptSourceKeyboard->interruptOccurred(0, 0, 0); +} + +#else // HANDLE_INTERRUPT_DATA_LATER + +void ApplePS2Controller::handleInterrupt(PS2DeviceType deviceType) +{ + ////IOLog("%s:handleInterrupt(%s)\n", getName(), deviceType == kDT_Keyboard ? "kDT_Keyboard" : deviceType == kDT_Watchdog ? "kDT_Watchdog" : "kDT_Mouse"); + + // Loop only while there is data currently on the input stream. + + UInt8 status; + IODelay(kDataDelay); + while ((status = inb(kCommandPort)) & kOutputReady) + { +#if WATCHDOG_TIMER + if (deviceType == kDT_Watchdog && (status & kMouseData)) + break; +#endif + + IODelay(kDataDelay); + UInt8 data = inb(kDataPort); +#if WATCHDOG_TIMER + //REVIEW: remove this debug eventually... + if (deviceType == kDT_Watchdog) + IOLog("%s:handleInterrupt(kDT_Watchdog): %s = %02x\n", getName(), status & kMouseData ? "mouse" : "keyboard", data); +#endif + dispatchDriverInterrupt(status & kMouseData ? kDT_Mouse : kDT_Keyboard, data); + IODelay(kDataDelay); + } +} + +#endif // HANDLE_INTERRUPT_DATA_LATER + +// ============================================================================= +// ApplePS2Controller Class Implementation +// + +OSDefineMetaClassAndStructors(ApplePS2Controller, IOService); + +bool ApplePS2Controller::init(OSDictionary* dict) +{ + if (!super::init(dict)) + return false; + +#if 0 + IOLog("PS2Request: %lu\n", sizeof(PS2Request)); + IOLog("TPS2Request<0>: %lu\n", sizeof(TPS2Request<0>)); + IOLog("TPS2Request<1>: %lu\n", sizeof(TPS2Request<1>)); + IOLog("offsetof(PS2Request,commands): %lu\n", offsetof(PS2Request, commands)); + IOLog("offsetof(TPS2Request<0>,commands): %lu\n", offsetof(TPS2Request<0>, commands)); + IOLog("offsetof(TPS2Request<1>,commands): %lu\n", offsetof(TPS2Request<1>, commands)); +#endif + // verify that compiler is working correctly wrt PS2Request/TPS2Request + if (sizeof(PS2Request) != sizeof(TPS2Request<0>)) + { + IOLog("ApplePS2Controller::init: PS2Request size mismatch (%lu != %lu)\n", + sizeof(PS2Request), sizeof(TPS2Request<0>)); + return false; + } + if (offsetof(PS2Request,commands) != offsetof(TPS2Request<>,commands)) + { + IOLog("ApplePS2Controller::init: PS2Request.commands offset mismatch (%lu != %lu)\n", + offsetof(PS2Request,commands), offsetof(PS2Request,commands)); + return false; + } + + // find config specific to Platform Profile + OSDictionary* list = OSDynamicCast(OSDictionary, dict->getObject(kPlatformProfile)); + OSDictionary* config = ApplePS2Controller::makeConfigurationNode(list); +#ifdef DEBUG + if (config) + setProperty(kMergedConfiguration, config); +#endif + + // + // Initialize minimal state. + // + + _workLoop = 0; + + _interruptSourceKeyboard = 0; + _interruptSourceMouse = 0; + _interruptTargetKeyboard = 0; + _interruptTargetMouse = 0; + _interruptActionKeyboard = NULL; + _interruptActionMouse = NULL; + _packetActionKeyboard = NULL; + _packetActionMouse = NULL; + _interruptInstalledKeyboard = false; + _interruptInstalledMouse = false; + _ignoreInterrupts = 0; + _ignoreOutOfOrder = 0; + + _powerControlTargetKeyboard = 0; + _powerControlTargetMouse = 0; + _powerControlActionKeyboard = 0; + _powerControlActionMouse = 0; + _powerControlInstalledKeyboard = false; + _powerControlInstalledMouse = false; + + _messageTargetKeyboard = 0; + _messageTargetMouse = 0; + _messageActionKeyboard = 0; + _messageActionMouse = 0; + _messageInstalledKeyboard = false; + _messageInstalledMouse = false; + + _mouseDevice = 0; + _keyboardDevice = 0; + + _suppressTimeout = false; + +#ifdef NEWIRQ + _newIRQLayout = false; // turbo +#endif + + _wakedelay = 10; + _mouseWakeFirst = false; + _cmdGate = 0; + + _requestQueueLock = 0; + _cmdbyteLock = 0; + +#if WATCHDOG_TIMER + _watchdogTimer = 0; +#endif + + queue_init(&_requestQueue); + + _currentPowerState = kPS2PowerStateNormal; + +#if DEBUGGER_SUPPORT + _extendedState = false; + _modifierState = 0x00; + _debuggingEnabled = false; + + _keyboardQueueAlloc = NULL; + queue_init(&_keyboardQueue); + queue_init(&_keyboardQueueUnused); + + _controllerLock = IOSimpleLockAlloc(); + if (!_controllerLock) return false; +#endif //DEBUGGER_SUPPORT + + setPropertiesGated(config); + OSSafeReleaseNULL(config); + + return true; +} + +#if DEBUGGER_SUPPORT +void ApplePS2Controller::free(void) +{ + if (_controllerLock) + { + IOSimpleLockFree(_controllerLock); + _controllerLock = 0; + } + super::free(); +} +#endif + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +IOReturn ApplePS2Controller::setPropertiesGated(OSObject* props) +{ + OSDictionary* dict = OSDynamicCast(OSDictionary, props); + if (!dict) + return kIOReturnSuccess; + + // get wakedelay + if (OSNumber* num = OSDynamicCast(OSNumber, dict->getObject("WakeDelay"))) + { + _wakedelay = (int)num->unsigned32BitValue(); + setProperty("WakeDelay", _wakedelay, 32); + } + // get mouseWakeFirst + if (OSBoolean* flag = OSDynamicCast(OSBoolean, dict->getObject("MouseWakeFirst"))) + { + _mouseWakeFirst = flag->isTrue(); + setProperty("MouseWakeFirst", _mouseWakeFirst); + } + return kIOReturnSuccess; +} + +IOReturn ApplePS2Controller::setProperties(OSObject* props) +{ + if (_cmdGate) + { + // syncronize through workloop... + IOReturn result = _cmdGate->runAction(OSMemberFunctionCast(IOCommandGate::Action, this, &ApplePS2Controller::setPropertiesGated), props); + if (kIOReturnSuccess != result) + return result; + } + return kIOReturnSuccess; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +void ApplePS2Controller::resetController(void) +{ + _suppressTimeout = true; + UInt8 commandByte; + + // Disable keyboard and mouse + writeCommandPort(kCP_DisableKeyboardClock); + writeCommandPort(kCP_DisableMouseClock); + // Flush any data + while ( inb(kCommandPort) & kOutputReady ) + { + IODelay(kDataDelay); + inb(kDataPort); + IODelay(kDataDelay); + } + writeCommandPort(kCP_EnableMouseClock); + writeCommandPort(kCP_EnableKeyboardClock); + // Read current command + writeCommandPort(kCP_GetCommandByte); + commandByte = readDataPort(kDT_Keyboard); + DEBUG_LOG("%s: initial commandByte = %02x\n", getName(), commandByte); + // Issue Test Controller to try to reset device + writeCommandPort(kCP_TestController); + readDataPort(kDT_Keyboard); + readDataPort(kDT_Mouse); + // Issue Test Keyboard Port to try to reset device + writeCommandPort(kCP_TestKeyboardPort); + readDataPort(kDT_Keyboard); + // Issue Test Mouse Port to try to reset device + writeCommandPort(kCP_TestMousePort); + readDataPort(kDT_Mouse); + _suppressTimeout = false; + + // + // Initialize the mouse and keyboard hardware to a known state -- the IRQs + // are disabled (don't want interrupts), the clock line is enabled (want to + // be able to send commands), and the device itself is disabled (don't want + // asynchronous data arrival for key/mouse events). We call the read/write + // port routines directly, since no other thread will conflict with us. + // + commandByte &= ~(kCB_EnableKeyboardIRQ | kCB_EnableMouseIRQ | kCB_DisableMouseClock | kCB_DisableMouseClock); + ////commandByte |= kCB_EnableKeyboardIRQ | kCB_EnableMouseIRQ; + commandByte |= kCB_TranslateMode; + writeCommandPort(kCP_SetCommandByte); + writeDataPort(commandByte); + DEBUG_LOG("%s: new commandByte = %02x\n", getName(), commandByte); + + writeDataPort(kDP_SetDefaultsAndDisable); + readDataPort(kDT_Keyboard); // (discard acknowledge; success irrelevant) + + writeCommandPort(kCP_TransmitToMouse); + writeDataPort(kDP_SetDefaultsAndDisable); + readDataPort(kDT_Mouse); // (discard acknowledge; success irrelevant) + + // + // Clear out garbage in the controller's input streams, before starting up + // the work loop. + // + + while ( inb(kCommandPort) & kOutputReady ) + { + IODelay(kDataDelay); + inb(kDataPort); + IODelay(kDataDelay); + } +} + +// -- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +bool ApplePS2Controller::start(IOService * provider) +{ + DEBUG_LOG("ApplePS2Controller::start entered...\n"); + + // + // The driver has been instructed to start. Allocate all our resources. + // + if (!super::start(provider)) + return false; + +#if DEBUGGER_SUPPORT + // Enable special key sequence to enter debugger if debug boot-arg was set. + int debugFlag = 0; +#ifdef TIGER + PE_parse_boot_arg("debug", &debugFlag); +#else + PE_parse_boot_argn("debug", &debugFlag, sizeof(debugFlag)); +#endif + if (debugFlag) _debuggingEnabled = true; + + _keyboardQueueAlloc = new KeyboardQueueElement[kKeyboardQueueSize]; + if (!_keyboardQueueAlloc) goto fail; + + // Add the allocated keyboard queue entries to "unused" queue. + for (int index = 0; index < kKeyboardQueueSize; index++) + queue_enter(&_keyboardQueueUnused, &_keyboardQueueAlloc[index], + KeyboardQueueElement *, chain); +#endif //DEBUGGER_SUPPORT + // Note: I don't think this newIRQLayout thing is used at all + // -- our provider is PS2Nub and the PS2 nub we use does not set this flag + // -- in addition it only supports the LEGACY interrupt specifiers + // #ifdef to eliminate for now... +#ifdef NEWIRQ + if (provider->getProperty("newIRQLayout")) { // turbo + IOLog("Using new IRQ layout 0,1\n"); + _newIRQLayout = true; + } +#endif + + // + // Reset and clean the 8042 keyboard/mouse controller. + // + + resetController(); + + // + // Use a spin lock to protect the client async request queue. + // + + _requestQueueLock = IOLockAlloc(); + if (!_requestQueueLock) goto fail; + _cmdbyteLock = IOLockAlloc(); + if (!_cmdbyteLock) goto fail; + + // + // Initialize our work loop, our command gate, and our interrupt event + // sources. The work loop can accept requests after this step. + // + + _workLoop = IOWorkLoop::workLoop(); +#if HANDLE_INTERRUPT_DATA_LATER + _interruptSourceMouse = IOInterruptEventSource::interruptEventSource( this, + OSMemberFunctionCast(IOInterruptEventAction, this, &ApplePS2Controller::interruptOccurred)); + _interruptSourceKeyboard = IOInterruptEventSource::interruptEventSource( this, + OSMemberFunctionCast(IOInterruptEventAction, this, &ApplePS2Controller::interruptOccurred)); +#else + _interruptSourceMouse = IOInterruptEventSource::interruptEventSource( this, + OSMemberFunctionCast(IOInterruptEventAction, this, &ApplePS2Controller::packetReadyMouse)); + _interruptSourceKeyboard = IOInterruptEventSource::interruptEventSource( this, + OSMemberFunctionCast(IOInterruptEventAction, this, &ApplePS2Controller::packetReadyKeyboard)); +#endif + _interruptSourceQueue = IOInterruptEventSource::interruptEventSource( this, + OSMemberFunctionCast(IOInterruptEventAction, this, &ApplePS2Controller::processRequestQueue)); + _cmdGate = IOCommandGate::commandGate(this); +#if WATCHDOG_TIMER + _watchdogTimer = IOTimerEventSource::timerEventSource(this, OSMemberFunctionCast(IOTimerEventSource::Action, this, &ApplePS2Controller::onWatchdogTimer)); + if (!_watchdogTimer) + goto fail; +#endif + + if ( !_workLoop || + !_interruptSourceMouse || + !_interruptSourceKeyboard || + !_interruptSourceQueue || + !_cmdGate) goto fail; + + if ( _workLoop->addEventSource(_interruptSourceQueue) != kIOReturnSuccess ) + goto fail; + if ( _workLoop->addEventSource(_cmdGate) != kIOReturnSuccess ) + goto fail; + +#if WATCHDOG_TIMER + if ( _workLoop->addEventSource(_watchdogTimer) != kIOReturnSuccess ) + goto fail; + _watchdogTimer->setTimeoutMS(kWatchdogTimerInterval); +#endif + _interruptSourceQueue->enable(); + + // + // Since there is a calling path from the PS/2 driver stack to power + // management for activity tickles. We must create a thread callout + // to handle power state changes from PM to avoid a deadlock. + // + + _powerChangeThreadCall = thread_call_allocate( + (thread_call_func_t) setPowerStateCallout, + (thread_call_param_t) this ); + if ( !_powerChangeThreadCall ) + goto fail; + + // + // Initialize our PM superclass variables and register as the power + // controlling driver. + // + + PMinit(); + + registerPowerDriver( this, (IOPMPowerState *) PS2PowerStateArray, + kPS2PowerStateCount ); + + // + // Insert ourselves into the PM tree. + // + + provider->joinPMtree(this); + + // + // Create the keyboard nub and the mouse nub. The keyboard and mouse drivers + // will query these nubs to determine the existence of the keyboard or mouse, + // and should they exist, will attach themselves to the nub as clients. + // + + _keyboardDevice = OSTypeAlloc(ApplePS2KeyboardDevice); + if ( !_keyboardDevice || + !_keyboardDevice->init() || + !_keyboardDevice->attach(this) ) + { + OSSafeReleaseNULL(_keyboardDevice); + OSSafeReleaseNULL(_interruptSourceKeyboard); + } + + _mouseDevice = OSTypeAlloc(ApplePS2MouseDevice); + if ( !_mouseDevice || + !_mouseDevice->init() || + !_mouseDevice->attach(this) ) + { + OSSafeReleaseNULL(_mouseDevice); + OSSafeReleaseNULL(_interruptSourceMouse); + } + + if (_keyboardDevice) + _keyboardDevice->registerService(); + if (_mouseDevice) + _mouseDevice->registerService(); + + registerService(); + + DEBUG_LOG("ApplePS2Controller::start leaving.\n"); + return true; // success + +fail: + stop(provider); + + DEBUG_LOG("ApplePS2Controller::start leaving(fail).\n"); + return false; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +void ApplePS2Controller::stop(IOService * provider) +{ + // + // The driver has been instructed to stop. Note that we must break all + // connections to other service objects now (ie. no registered actions, + // no pointers and retains to objects, etc), if any. + // + + // Ensure that the interrupt handlers have been uninstalled (ie. no clients). + assert(!_interruptInstalledKeyboard); + assert(!_interruptInstalledMouse); + assert(!_powerControlInstalledKeyboard); + assert(!_powerControlInstalledMouse); + + // Free the nubs we created. + OSSafeReleaseNULL(_keyboardDevice); + OSSafeReleaseNULL(_mouseDevice); + + // Free the event/interrupt sources. + OSSafeReleaseNULL(_interruptSourceKeyboard); + OSSafeReleaseNULL(_interruptSourceMouse); + OSSafeReleaseNULL(_interruptSourceQueue); + OSSafeReleaseNULL(_cmdGate); +#if WATCHDOG_TIMER + OSSafeReleaseNULL(_watchdogTimer); +#endif + + // Free the work loop. + OSSafeReleaseNULL(_workLoop); + + // Free the request queue lock and empty out the request queue. + if (_requestQueueLock) + { + _hardwareOffline = true; + processRequestQueue(0, 0); + IOLockFree(_requestQueueLock); + _requestQueueLock = 0; + } + if (_cmdbyteLock) + { + IOLockFree(_cmdbyteLock); + _cmdbyteLock = 0; + } + + // Free the power management thread call. + if (_powerChangeThreadCall) + { + thread_call_free(_powerChangeThreadCall); + _powerChangeThreadCall = 0; + } + + // Detach from power management plane. + PMstop(); + +#if DEBUGGER_SUPPORT + // Free the keyboard queue allocation space (after disabling interrupt). + if (_keyboardQueueAlloc) + { + delete[] _keyboardQueueAlloc; + _keyboardQueueAlloc = 0; + } +#endif //DEBUGGER_SUPPORT + + super::stop(provider); +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +IOWorkLoop * ApplePS2Controller::getWorkLoop() const +{ + return _workLoop; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +void ApplePS2Controller::installInterruptAction(PS2DeviceType deviceType, + OSObject * target, + PS2InterruptAction interruptAction, + PS2PacketAction packetAction) +{ + // + // Install the keyboard or mouse interrupt handler. + // + // This method assumes only one possible mouse and only one possible + // keyboard client (ie. callers), and assumes two distinct interrupt + // handlers for each, hence needs no protection against races. + // + + // Is it the keyboard or the mouse interrupt handler that was requested? + // We only install it if it is currently uninstalled. + + if (deviceType == kDT_Keyboard && !_interruptInstalledKeyboard && _interruptSourceKeyboard) + { + target->retain(); + _interruptTargetKeyboard = target; + _interruptActionKeyboard = interruptAction; + _packetActionKeyboard = packetAction; + _workLoop->addEventSource(_interruptSourceKeyboard); + DEBUG_LOG("%s: setCommandByte for keyboard interrupt install\n", getName()); + setCommandByte(kCB_EnableKeyboardIRQ, 0); +#ifdef NEWIRQ + if (_newIRQLayout) + { // turbo + getProvider()->registerInterrupt(0,0, interruptHandlerKeyboard, this); + getProvider()->enableInterrupt(0); + } else +#endif + { + getProvider()->registerInterrupt(kIRQ_Keyboard,0, interruptHandlerKeyboard, this); + getProvider()->enableInterrupt(kIRQ_Keyboard); + } + + _interruptInstalledKeyboard = true; + } + else if (deviceType == kDT_Mouse && !_interruptInstalledMouse && _interruptSourceMouse) + { + target->retain(); + _interruptTargetMouse = target; + _interruptActionMouse = interruptAction; + _packetActionMouse = packetAction; + _workLoop->addEventSource(_interruptSourceMouse); + DEBUG_LOG("%s: setCommandByte for mouse interrupt install\n", getName()); + setCommandByte(kCB_EnableMouseIRQ, 0); +#ifdef NEWIRQ + if (_newIRQLayout) + { // turbo + getProvider()->registerInterrupt(1, 0, interruptHandlerMouse, this); + getProvider()->enableInterrupt(1); + } else +#endif + { + getProvider()->registerInterrupt(kIRQ_Mouse, 0, interruptHandlerMouse, this); + getProvider()->enableInterrupt(kIRQ_Mouse); + } + + _interruptInstalledMouse = true; + } +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +void ApplePS2Controller::uninstallInterruptAction(PS2DeviceType deviceType) +{ + // + // Uninstall the keyboard or mouse interrupt handler. + // + // This method assumes only one possible mouse and only one possible + // keyboard client (ie. callers), and assumes two distinct interrupt + // handlers for each, hence needs no protection against races. + // + + // Is it the keyboard or the mouse interrupt handler that was requested? + // We only install it if it is currently uninstalled. + + if (deviceType == kDT_Keyboard && _interruptInstalledKeyboard) + { + setCommandByte(0, kCB_EnableKeyboardIRQ); +#ifdef NEWIRQ + getProvider()->disableInterrupt(0); + getProvider()->unregisterInterrupt(0); +#else + getProvider()->disableInterrupt(kIRQ_Keyboard); + getProvider()->unregisterInterrupt(kIRQ_Keyboard); +#endif + _workLoop->removeEventSource(_interruptSourceKeyboard); + _interruptInstalledKeyboard = false; + _interruptActionKeyboard = NULL; + _packetActionKeyboard = NULL; + _interruptTargetKeyboard->release(); + _interruptTargetKeyboard = 0; + } + + else if (deviceType == kDT_Mouse && _interruptInstalledMouse) + { + setCommandByte(0, kCB_EnableMouseIRQ); +#ifdef NEWIRQ + getProvider()->disableInterrupt(1); + getProvider()->unregisterInterrupt(1); +#else + getProvider()->disableInterrupt(kIRQ_Mouse); + getProvider()->unregisterInterrupt(kIRQ_Mouse); +#endif + _workLoop->removeEventSource(_interruptSourceMouse); + _interruptInstalledMouse = false; + _interruptActionMouse = NULL; + _packetActionMouse = NULL; + _interruptTargetMouse->release(); + _interruptTargetMouse = 0; + } +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +PS2Request * ApplePS2Controller::allocateRequest(int max) +{ + // + // Allocate a request structure. Blocks until successful. + // Most of request structure is guaranteed to be zeroed. + // + + assert(max > 0); + + return new(max) PS2Request; +} + +EXPORT PS2Request::PS2Request() +{ + commandsCount = 0; + completionTarget = 0; + completionAction = 0; + completionParam = 0; + +#ifdef DEBUG + // These items do not need to be initialized, but it might make it easier to + // debug if they start at zero. + chain.prev = 0; + chain.next = 0; +#endif +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +void ApplePS2Controller::freeRequest(PS2Request * request) +{ + // + // Deallocate a request structure. + // + + delete request; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +UInt8 ApplePS2Controller::setCommandByte(UInt8 setBits, UInt8 clearBits) +{ + TPS2Request<1> request; + request.commands[0].command = kPS2C_ModifyCommandByte; + request.commands[0].setBits = setBits; + request.commands[0].clearBits = clearBits; + request.commandsCount = 1; + assert(request.commandsCount <= countof(request.commands)); + _cmdGate->runAction(OSMemberFunctionCast(IOCommandGate::Action, this, &ApplePS2Controller::setCommandByteGated), &request); + return request.commands[0].oldBits; +} + +void ApplePS2Controller::setCommandByteGated(PS2Request* request) +{ + UInt8 setBits = request->commands[0].setBits; + UInt8 clearBits = request->commands[0].clearBits; + ++_ignoreInterrupts; + writeCommandPort(kCP_GetCommandByte); + UInt8 oldCommandByte = readDataPort(kDT_Keyboard); + --_ignoreInterrupts; + DEBUG_LOG("%s: oldCommandByte = %02x\n", getName(), oldCommandByte); + UInt8 newCommandByte = (oldCommandByte | setBits) & ~clearBits; + if (oldCommandByte != newCommandByte) + { + DEBUG_LOG("%s: newCommandByte = %02x\n", getName(), newCommandByte); + writeCommandPort(kCP_SetCommandByte); + writeDataPort(newCommandByte); + } + request->commands[0].oldBits = oldCommandByte; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +bool ApplePS2Controller::submitRequest(PS2Request * request) +{ + // + // Submit the request to the controller for processing, asynchronously. + // + + IOLockLock(_requestQueueLock); + queue_enter(&_requestQueue, request, PS2Request *, chain); + IOLockUnlock(_requestQueueLock); + + _interruptSourceQueue->interruptOccurred(0, 0, 0); + + return true; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +void ApplePS2Controller::submitRequestAndBlock(PS2Request * request) +{ + _cmdGate->runAction(OSMemberFunctionCast(IOCommandGate::Action, this, &ApplePS2Controller::submitRequestAndBlockGated), request); +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +void ApplePS2Controller::submitRequestAndBlockGated(PS2Request* request) +{ + processRequestQueue(0, 0); + processRequest(request); +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +#if HANDLE_INTERRUPT_DATA_LATER +void ApplePS2Controller::interruptOccurred(IOInterruptEventSource* source, int) +{ // IOInterruptEventAction + // + // Our work loop has informed us of an interrupt, that is, asynchronous + // data has arrived on our input stream. Read the data and dispatch it + // to the appropriate driver. + // + // This method should only be called from our single-threaded work loop. + // + + if (_hardwareOffline) + { + // Toss any asynchronous data received. The interrupt event source may + // have been signalled before the PS/2 port was offline. + return; + } + +#if DEBUGGER_SUPPORT + UInt8 status; + int state; + lockController(&state); // (lock out interrupt + access to queue) + while (1) + { + // See if data is available on the keyboard input stream (off queue); + // we do not read keyboard data from the real data port if it should + // be available. + + if (dequeueKeyboardData(&status)) + { + unlockController(state); + dispatchDriverInterrupt(kDT_Keyboard, status); + lockController(&state); + } + + // See if data is available on the mouse input stream (off real port). + + else if ( (inb(kCommandPort) & (kOutputReady | kMouseData)) == + (kOutputReady | kMouseData)) + { + unlockController(state); + IODelay(kDataDelay); + dispatchDriverInterrupt(kDT_Mouse, inb(kDataPort)); + lockController(&state); + } + else break; // out of loop + } + unlockController(state); // (release interrupt lockout + access to queue) +#else + handleInterrupt(source == _interruptSourceKeyboard ? kDT_Keyboard : kDT_Mouse); +#endif // DEBUGGER_SUPPORT +} +#endif // HANDLE_INTERRUPT_DATA_LATER + +#if !HANDLE_INTERRUPT_DATA_LATER +void ApplePS2Controller::packetReadyKeyboard(IOInterruptEventSource *, int) +{ + // a complete packet has arrived for the keyboard and has signaled the workloop + // -- dispatch it to the installed keyboard packet handler + if (_interruptInstalledKeyboard) + (*_packetActionKeyboard)(_interruptTargetKeyboard); +} + +void ApplePS2Controller::packetReadyMouse(IOInterruptEventSource *, int) +{ + // a complete packet has arrived for the mouse and has signaled the workloop + // -- dispatch it to the installed mouse packet handler + if (_interruptInstalledMouse) + (*_packetActionMouse)(_interruptTargetMouse); +} +#endif // !HANDLE_INTERRUPT_DATA_LATER + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +PS2InterruptResult ApplePS2Controller::_dispatchDriverInterrupt(PS2DeviceType deviceType, UInt8 data) +{ + PS2InterruptResult result = kPS2IR_packetBuffering; + if (kDT_Mouse == deviceType && _interruptInstalledMouse) + { + // Dispatch the data to the mouse driver. + result = (*_interruptActionMouse)(_interruptTargetMouse, data); + } + else if (kDT_Keyboard == deviceType && _interruptInstalledKeyboard) + { + // Dispatch the data to the keyboard driver. + result = (*_interruptActionKeyboard)(_interruptTargetKeyboard, data); + } + return result; +} + +void ApplePS2Controller::dispatchDriverInterrupt(PS2DeviceType deviceType, UInt8 data) +{ + PS2InterruptResult result = _dispatchDriverInterrupt(deviceType, data); + if (kPS2IR_packetReady == result) + { +#if HANDLE_INTERRUPT_DATA_LATER + if (kDT_Mouse == deviceType) + (*_packetActionMouse)(_interruptTargetMouse); + else if (kDT_Keyboard == deviceType) + (*_packetActionKeyboard)(_interruptTargetKeyboard); +#else + if (kDT_Mouse == deviceType) + _interruptSourceMouse->interruptOccurred(0, 0, 0); + else if (kDT_Keyboard == deviceType) + _interruptSourceKeyboard->interruptOccurred(0, 0, 0); +#endif + } +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +void ApplePS2Controller::processRequest(PS2Request * request) +{ + // + // Our work loop has informed us of a request submission. Process + // the request. Note that this code "figures out" when the mouse + // input stream should be read over the keyboard input stream. + // + // This method should only be called from our single-threaded work loop. + // + + UInt8 byte; + PS2DeviceType deviceMode = kDT_Keyboard; + bool failed = false; + bool transmitToMouse = false; + unsigned index; + + if (_hardwareOffline) + { + failed = true; + index = 0; + goto hardware_offline; + } + + // Don't handle interrupts during this process. We want to read the + // data by polling for it here. + + ++_ignoreInterrupts; + + // Process each of the commands in the list. + + for (index = 0; index < request->commandsCount; index++) + { + switch (request->commands[index].command) + { + case kPS2C_ReadDataPort: + request->commands[index].inOrOut = readDataPort(deviceMode); + break; + + case kPS2C_ReadDataPortAndCompare: +#if OUT_OF_ORDER_DATA_CORRECTION_FEATURE + byte = readDataPort(deviceMode, request->commands[index].inOrOut); +#else + byte = readDataPort(deviceMode); +#endif + failed = (byte != request->commands[index].inOrOut); + request->commands[index].inOrOut = byte; + break; + + case kPS2C_WriteDataPort: + writeDataPort(request->commands[index].inOrOut); + if (transmitToMouse) // next reads from mouse input stream + { + deviceMode = kDT_Mouse; + transmitToMouse = false; + } + else + { + deviceMode = kDT_Keyboard; + } + break; + + case kPS2C_WriteCommandPort: + writeCommandPort(request->commands[index].inOrOut); + if (request->commands[index].inOrOut == kCP_TransmitToMouse) + transmitToMouse = true; // preparing to transmit data to mouse + break; + + // + // Send a composite mouse command that is equivalent to the following + // (frequently used) command sequence: + // + // 1. kPS2C_WriteCommandPort( kCP_TransmitToMouse ) + // 2. kPS2C_WriteDataPort( command ) + // 3. kPS2C_ReadDataPortAndCompare( kSC_Acknowledge ) + // + + case kPS2C_SendMouseCommandAndCompareAck: + writeCommandPort(kCP_TransmitToMouse); + writeDataPort(request->commands[index].inOrOut); + deviceMode = kDT_Mouse; +#if OUT_OF_ORDER_DATA_CORRECTION_FEATURE + byte = readDataPort(kDT_Mouse, kSC_Acknowledge); +#else + byte = readDataPort(kDT_Mouse); +#endif + failed = (byte != kSC_Acknowledge); + break; + + case kPS2C_ReadMouseDataPort: + deviceMode= kDT_Mouse; + request->commands[index].inOrOut = readDataPort(deviceMode); + break; + + case kPS2C_ReadMouseDataPortAndCompare: + deviceMode= kDT_Mouse; +#if OUT_OF_ORDER_DATA_CORRECTION_FEATURE + byte = readDataPort(deviceMode, request->commands[index].inOrOut); +#else + byte = readDataPort(deviceMode); +#endif + failed = (byte != request->commands[index].inOrOut); + break; + + case kPS2C_FlushDataPort: + request->commands[index].inOrOut32 = 0; + while ( inb(kCommandPort) & kOutputReady ) + { + ++request->commands[index].inOrOut32; + IODelay(kDataDelay); + inb(kDataPort); + IODelay(kDataDelay); + } + break; + + case kPS2C_SleepMS: + IOSleep(request->commands[index].inOrOut32); + break; + + case kPS2C_ModifyCommandByte: + writeCommandPort(kCP_GetCommandByte); + UInt8 commandByte = readDataPort(kDT_Keyboard); + writeCommandPort(kCP_SetCommandByte); + writeDataPort((commandByte | request->commands[index].setBits) & ~request->commands[index].clearBits); + request->commands[index].oldBits = commandByte; + break; + } + + if (failed) break; + } + + // Now it is ok to process interrupts normally. + + --_ignoreInterrupts; + +hardware_offline: + + // If a command failed and stopped the request processing, store its + // index into the commandsCount field. + + if (failed) request->commandsCount = index; + + // Invoke the completion routine, if one was supplied. + + if (request->completionTarget != kStackCompletionTarget && request->completionTarget && request->completionAction) { + (*request->completionAction)(request->completionTarget, + request->completionParam); + } + else + { + if (request->completionTarget != kStackCompletionTarget) + freeRequest(request); + } +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +void ApplePS2Controller::processRequestQueue(IOInterruptEventSource *, int) +{ + queue_head_t localQueue; + + // Transfer queued (async) requests to a local queue. + + IOLockLock(_requestQueueLock); + + if (!queue_empty(&_requestQueue)) + { + queue_assign(&localQueue, &_requestQueue, PS2Request *, chain); + queue_init(&_requestQueue); + } + else queue_init(&localQueue); + + IOLockUnlock(_requestQueueLock); + + // Process each request in order. + + while (!queue_empty(&localQueue)) + { + PS2Request * request; + queue_remove_first(&localQueue, request, PS2Request *, chain); + processRequest(request); + } +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +UInt8 ApplePS2Controller::readDataPort(PS2DeviceType deviceType) +{ + // + // Blocks until keyboard or mouse data is available from the controller + // and returns that data. Note, if mouse data is requested but keyboard + // data is what is available, the data is delivered to the appropriate + // driver interrupt routine immediately (effectively, the request is + // "preempted" temporarily). + // + // There is a built-in timeout for this command of (timeoutCounter X + // kDataDelay) microseconds, approximately. + // + // This method should only be called from our single-threaded work loop. + // + + UInt8 readByte; + UInt8 status; + UInt32 timeoutCounter = 10000; // (timeoutCounter * kDataDelay = 70 ms) + + while (1) + { +#if DEBUGGER_SUPPORT + int state; + lockController(&state); // (lock out interrupt + access to queue) + if (deviceType == kDT_Keyboard && dequeueKeyboardData(&readByte)) + { + unlockController(state); + return readByte; + } +#endif //DEBUGGER_SUPPORT + + // + // Wait for the controller's output buffer to become ready. + // + + while (timeoutCounter && !((status = inb(kCommandPort)) & kOutputReady)) + { + timeoutCounter--; + IODelay(kDataDelay); + } + + // + // If we timed out, something went awfully wrong; return a fake value. + // + + if (timeoutCounter == 0) + { +#if DEBUGGER_SUPPORT + unlockController(state); // (release interrupt lockout + access to queue) +#endif //DEBUGGER_SUPPORT + + if (!_suppressTimeout) + IOLog("%s: Timed out on %s input stream.\n", getName(), + (deviceType == kDT_Keyboard) ? "keyboard" : "mouse"); + return 0; + } + + // + // For older machines, it is necessary to wait a while after the controller + // has asserted the output buffer bit before reading the data port. No more + // data will be available if this wait is not performed. + // + + IODelay(kDataDelay); + + // + // Read in the data. We return the data, however, only if it arrived on + // the requested input stream. + // + + readByte = inb(kDataPort); + +#if DEBUGGER_SUPPORT + unlockController(state); // (release interrupt lockout + access to queue) +#endif //DEBUGGER_SUPPORT + + if (_suppressTimeout) // startup mode w/o interrupts + return readByte; + + if ( (status & kMouseData) ) + { + if (deviceType == kDT_Mouse) return readByte; + } + else + { + if (deviceType == kDT_Keyboard) return readByte; + } + + // + // The data we just received is for the other input stream, not the one + // that was requested, so dispatch other device's interrupt handler. + // + + dispatchDriverInterrupt((deviceType==kDT_Keyboard)?kDT_Mouse:kDT_Keyboard, + readByte); + } // while (forever) +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +#if OUT_OF_ORDER_DATA_CORRECTION_FEATURE + +UInt8 ApplePS2Controller::readDataPort(PS2DeviceType deviceType, + UInt8 expectedByte) +{ + // + // Blocks until keyboard or mouse data is available from the controller + // and returns that data. Note, if mouse data is requested but keyboard + // data is what is available, the data is delivered to the appropriate + // driver interrupt routine immediately (effectively, the request is + // "preempted" temporarily). + // + // There is a built-in timeout for this command of (timeoutCounter X + // kDataDelay) microseconds, approximately. + // + // This method should only be called from our single-threaded work loop. + // + // This version of readDataPort does exactly the same as the original, + // except that if the value that should be read from the (appropriate) + // input stream is not what is expected, we make these assumptions: + // + // (a) the data byte we did get was "asynchronous" data being sent by + // the device, which has not figured out that it has to respond to + // the command we just sent to it. + // (b) that the real "expected" response will be the next byte in the + // stream; so what we do is put aside the first byte we read and + // wait for the next byte; if it's the expected value, we dispatch + // the first byte we read to the driver's interrupt handler, then + // return the expected byte. The caller will have never known that + // asynchronous data arrived at a very bad time. + // (c) that the real "expected" response will arrive within (kDataDelay + // X timeoutCounter) microseconds from the time the call is made. + // + + UInt8 firstByte = 0; + bool firstByteHeld = false; + UInt8 readByte; + bool requestedStream; + UInt8 status; + UInt32 timeoutCounter = 10000; // (timeoutCounter * kDataDelay = 70 ms) + + while (1) + { +#if DEBUGGER_SUPPORT + int state; + lockController(&state); // (lock out interrupt + access to queue) + if (deviceType == kDT_Keyboard && dequeueKeyboardData(&readByte)) + { + requestedStream = true; + goto skipForwardToY; + } +#endif //DEBUGGER_SUPPORT + + // + // Wait for the controller's output buffer to become ready. + // + + while (timeoutCounter && !((status = inb(kCommandPort)) & kOutputReady)) + { + timeoutCounter--; + IODelay(kDataDelay); + } + + // + // If we timed out, we return the first byte we read, unless THIS IS the + // first byte we are trying to read, then something went awfully wrong + // and we return a fake value rather than lock up the controller longer. + // + + if (timeoutCounter == 0) + { +#if DEBUGGER_SUPPORT + unlockController(state); // (release interrupt lockout + access to queue) +#endif //DEBUGGER_SUPPORT + + if (firstByteHeld) return firstByte; + + IOLog("%s: Timed out on %s input stream.\n", getName(), + (deviceType == kDT_Keyboard) ? "keyboard" : "mouse"); + return 0; + } + + // + // For older machines, it is necessary to wait a while after the controller + // has asserted the output buffer bit before reading the data port. No more + // data will be available if this wait is not performed. + // + + IODelay(kDataDelay); + + // + // Read in the data. We process the data, however, only if it arrived on + // the requested input stream. + // + + readByte = inb(kDataPort); + requestedStream = false; + + if ( (status & kMouseData) ) + { + if (deviceType == kDT_Mouse) requestedStream = true; + } + else + { + if (deviceType == kDT_Keyboard) requestedStream = true; + } + +#if DEBUGGER_SUPPORT + skipForwardToY: + unlockController(state); // (release interrupt lockout + access to queue) +#endif //DEBUGGER_SUPPORT + + if (requestedStream) + { + if (readByte == expectedByte) + { + if (firstByteHeld == false) + { + // + // Normal case. Return first byte received. + // + + return readByte; + } + else + { + // + // Our assumption was correct. The second byte matched. Dispatch + // the first byte to the interrupt handler, and return the second. + // + + if (!_ignoreOutOfOrder) + dispatchDriverInterrupt(deviceType, firstByte); + return readByte; + } + } + else // (readByte does not match expectedByte) + { + if (firstByteHeld == false) + { + // + // The first byte was received, and does not match the byte we are + // expecting. Put it aside for the moment. + // + + firstByteHeld = true; + firstByte = readByte; + } + else if (readByte != expectedByte) + { + // + // The second byte mismatched as well. I have yet to see this case + // occur [Dan], however I do think it's plausible. No error logged. + // + + if (!_ignoreOutOfOrder) + dispatchDriverInterrupt(deviceType, readByte); + return firstByte; + } + } + } + else + { + // + // The data we just received is for the other input stream, not ours, + // so dispatch appropriate interrupt handler. + // + + if (!_ignoreOutOfOrder) + dispatchDriverInterrupt(deviceType == kDT_Keyboard ? kDT_Mouse : kDT_Keyboard, readByte); + } + } // while (forever) +} + +#endif + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +void ApplePS2Controller::writeDataPort(UInt8 byte) +{ + // + // Block until room in the controller's input buffer is available, then + // write the given byte to the Data Port. + // + // This method should only be dispatched from our single-threaded work loop. + // + + while (inb(kCommandPort) & kInputBusy) + IODelay(kDataDelay); + IODelay(kDataDelay); + outb(kDataPort, byte); +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +void ApplePS2Controller::writeCommandPort(UInt8 byte) +{ + // + // Block until room in the controller's input buffer is available, then + // write the given byte to the Command Port. + // + // This method should only be dispatched from our single-threaded work loop. + // + + while (inb(kCommandPort) & kInputBusy) + IODelay(kDataDelay); + IODelay(kDataDelay); + outb(kCommandPort, byte); +} + +// ============================================================================= +// Escape-Key Processing Stuff Localized Here (eg. Mini-Monitor) +// + +#if DEBUGGER_SUPPORT + +#define kModifierShiftLeft 0x01 +#define kModifierShiftRight 0x02 +#define kModifierCtrlLeft 0x04 +#define kModifierCtrlRight 0x08 +#define kModifierAltLeft 0x10 +#define kModifierAltRight 0x20 +#define kModifierWindowsLeft 0x40 +#define kModifierWindowsRight 0x80 + +#define kModifierShiftMask (kModifierShiftLeft | kModifierShiftRight ) +#define kModifierCtrlMask (kModifierCtrlLeft | kModifierCtrlRight ) +#define kModifierAltMask (kModifierAltLeft | kModifierAltRight ) +#define kModifierWindowsMask (kModifierWindowsLeft | kModifierWindowsRight) + +bool ApplePS2Controller::doEscape(UInt8 scancode) +{ + static struct + { + UInt8 scancode; + UInt8 extended; + UInt16 modifier; + } modifierTable[] = { { kSC_Alt, false, kModifierAltLeft }, + { kSC_Alt, true, kModifierAltRight }, + { kSC_Ctrl, false, kModifierCtrlLeft }, + { kSC_Ctrl, true, kModifierCtrlRight }, + { kSC_ShiftLeft, false, kModifierShiftLeft }, + { kSC_ShiftRight, false, kModifierShiftRight }, + { kSC_WindowsLeft, true, kModifierWindowsLeft }, + { kSC_WindowsRight, true, kModifierWindowsRight }, + { 0, 0, 0 } }; + + UInt32 index; + bool releaseModifiers = false; + bool upBit = (scancode & kSC_UpBit) ? true : false; + + // + // See if this is an extened scancode sequence. + // + + if (scancode == kSC_Extend) + { + _extendedState = true; + return false; + } + + // + // Update the modifier state, if applicable. + // + + scancode &= ~kSC_UpBit; + + for (index = 0; modifierTable[index].scancode; index++) + { + if ( modifierTable[index].scancode == scancode && + modifierTable[index].extended == _extendedState ) + { + if (upBit) _modifierState &= ~modifierTable[index].modifier; + else _modifierState |= modifierTable[index].modifier; + + _extendedState = false; + return false; + } + } + + // + // Call the debugger function, if applicable. + // + + if (scancode == kSC_Delete) // (both extended and non-extended scancodes) + { + if ( _modifierState == (kModifierAltLeft | kModifierAltRight) ) + { + // Disable the mouse by forcing the clock line low. + + while (inb(kCommandPort) & kInputBusy) + IODelay(kDataDelay); + IODelay(kDataDelay); + outb(kCommandPort, kCP_DisableMouseClock); + + // Call the debugger function. + + Debugger("Programmer Key"); + + // Re-enable the mouse by making the clock line active. + + while (inb(kCommandPort) & kInputBusy) + IODelay(kDataDelay); + IODelay(kDataDelay); + outb(kCommandPort, kCP_EnableMouseClock); + + releaseModifiers = true; + } + } + + // + // Release all the modifier keys that were down before the debugger + // function was called (assumption is that they are no longer held + // down after the debugger function returns). + // + + if (releaseModifiers) + { + for (index = 0; modifierTable[index].scancode; index++) + { + if ( _modifierState & modifierTable[index].modifier ) + { + if (modifierTable[index].extended) enqueueKeyboardData(kSC_Extend); + enqueueKeyboardData(modifierTable[index].scancode | kSC_UpBit); + } + } + _modifierState = 0x00; + } + + // + // Update all other state and return status. + // + + _extendedState = false; + return (releaseModifiers); +} + +void ApplePS2Controller::enqueueKeyboardData(UInt8 key) +{ + // + // Enqueue the supplied keyboard data onto our internal queues. The + // controller must already be locked. + // + + KeyboardQueueElement * element; + + // Obtain an unused keyboard data element. + if (!queue_empty(&_keyboardQueueUnused)) + { + queue_remove_first(&_keyboardQueueUnused, + element, KeyboardQueueElement *, chain); + + // Store the new keyboard data element on the queue. + element->data = key; + queue_enter(&_keyboardQueue, element, KeyboardQueueElement *, chain); + } +} + +bool ApplePS2Controller::dequeueKeyboardData(UInt8 * key) +{ + // + // Dequeue keyboard data from our internal queues, if the queue is not + // empty. Should the queue be empty, false is returned. The controller + // must already be locked. + // + + KeyboardQueueElement * element; + + // Obtain an unused keyboard data element. + if (!queue_empty(&_keyboardQueue)) + { + queue_remove_first(&_keyboardQueue, element, KeyboardQueueElement *, chain); + *key = element->data; + + // Place the unused keyboard data element onto the unused queue. + queue_enter(&_keyboardQueueUnused, element, KeyboardQueueElement *, chain); + + return true; + } + return false; +} + +void ApplePS2Controller::unlockController(int state) +{ + IOSimpleLockUnlockEnableInterrupt(_controllerLock, state); +} + +void ApplePS2Controller::lockController(int * state) +{ + *state = IOSimpleLockLockDisableInterrupt(_controllerLock); +} + +#endif //DEBUGGER_SUPPORT + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +// +// Power Management support. +// +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +IOReturn ApplePS2Controller::setPowerState( unsigned long powerStateOrdinal, + IOService * policyMaker) +{ + IOReturn result = IOPMAckImplied; + + // + // Prevent the object from being freed while a call is pending. + // If thread_call_enter() returns TRUE, indicating that a call + // is already pending, then the extra retain is dropped. + // + + retain(); + if ( thread_call_enter1( _powerChangeThreadCall, + (void *) powerStateOrdinal ) == TRUE ) + { + release(); + } + result = 5000000; // 5 seconds before acknowledgement timeout + + return result; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +void ApplePS2Controller::setPowerStateCallout( thread_call_param_t param0, + thread_call_param_t param1 ) +{ + ApplePS2Controller * me = (ApplePS2Controller *) param0; + assert(me); + + if (me->_workLoop) + { + me->_workLoop->runAction( /* Action */ setPowerStateAction, + /* target */ me, + /* arg0 */ param1 ); + } + + me->release(); // drop the retain from setPowerState() +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +IOReturn ApplePS2Controller::setPowerStateAction( OSObject * target, + void * arg0, void * arg1, + void * arg2, void * arg3 ) +{ + ApplePS2Controller * me = (ApplePS2Controller *) target; + +#ifdef __LP64__ + UInt32 powerState = (UInt32)(UInt64)arg0; +#else + UInt32 powerState = (UInt32) arg0; +#endif + + me->setPowerStateGated( powerState ); + + return kIOReturnSuccess; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +void ApplePS2Controller::setPowerStateGated( UInt32 powerState ) +{ + if ( _currentPowerState != powerState ) + { + switch ( powerState ) + { + case kPS2PowerStateSleep: + + // + // 1. Make sure clocks are enabled, but IRQ lines held low. + // + + ++_ignoreInterrupts; + DEBUG_LOG("%s: setCommandByte for sleep 1\n", getName()); + setCommandByte(0, kCB_EnableKeyboardIRQ | kCB_EnableMouseIRQ); + + // 2. Notify clients about the state change. Clients can issue + // synchronous requests thanks to the recursive lock. + // First Mouse, then Keyboard. + + dispatchDriverPowerControl( kPS2C_DisableDevice, kDT_Mouse ); + dispatchDriverPowerControl( kPS2C_DisableDevice, kDT_Keyboard ); + + // 3. Freeze the request queue and drop all data received over + // the PS/2 port. + + _hardwareOffline = true; + + // 4. Disable the PS/2 port. + +#if DISABLE_CLOCKS_IRQS_BEFORE_SLEEP + // This will cause some machines to turn on the LCD after the + // ACPI display driver has turned it off. With a real display + // driver present, this block of code can be uncommented (?). + + DEBUG_LOG("%s: setCommandByte for sleep 2\n", getName()); + setCommandByte(kCB_DisableKeyboardClock | kCB_DisableMouseClock, 0); +#endif // DISABLE_CLOCKS_IRQS_BEFORE_SLEEP + break; + + case kPS2PowerStateDoze: + case kPS2PowerStateNormal: + + if ( _currentPowerState != kPS2PowerStateSleep ) + { + // Transitions between doze and normal power states + // require no action, since both are working states. + break; + } + + if (_wakedelay) + IOSleep(_wakedelay); + +#if FULL_INIT_AFTER_WAKE + // + // Reset and clean the 8042 keyboard/mouse controller. + // + + resetController(); + +#endif // FULL_INIT_AFTER_WAKE + + + // + // Transition from Sleep state to Working state in 4 stages. + // + + // 1. Enable the PS/2 port -- but just the clocks + + DEBUG_LOG("%s: setCommandByte for wake 1\n", getName()); + setCommandByte(0, kCB_DisableKeyboardClock | kCB_DisableMouseClock | kCB_EnableKeyboardIRQ | kCB_EnableMouseIRQ); + + // 2. Unblock the request queue and wake up all driver threads + // that were blocked by submitRequest(). + + _hardwareOffline = false; + + // 3. Notify clients about the state change: Keyboard, then Mouse. + // (This ordering is also part of the fix for ProBook 4x40s trackpad wake issue) + // The ordering can be reversed from normal by setting MouseWakeFirst=true + + if (!_mouseWakeFirst) + { + dispatchDriverPowerControl( kPS2C_EnableDevice, kDT_Keyboard ); + dispatchDriverPowerControl( kPS2C_EnableDevice, kDT_Mouse ); + } + else + { + dispatchDriverPowerControl( kPS2C_EnableDevice, kDT_Mouse ); + dispatchDriverPowerControl( kPS2C_EnableDevice, kDT_Keyboard ); + } + + // 4. Now safe to enable the IRQs... + + DEBUG_LOG("%s: setCommandByte for wake 2\n", getName()); + setCommandByte(kCB_EnableKeyboardIRQ | kCB_EnableMouseIRQ | kCB_SystemFlag, 0); + --_ignoreInterrupts; + break; + + default: + IOLog("%s: bad power state %ld\n", getName(), (long)powerState); + break; + } + + _currentPowerState = powerState; + } + + // + // Acknowledge the power change before the power management timeout + // expires. + // + + acknowledgeSetPowerState(); +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +void ApplePS2Controller::dispatchDriverPowerControl( UInt32 whatToDo, PS2DeviceType deviceType ) +{ + if (kDT_Mouse == deviceType && _powerControlInstalledMouse) + (*_powerControlActionMouse)(_powerControlTargetMouse, whatToDo); + + if (kDT_Keyboard == deviceType && _powerControlInstalledKeyboard) + (*_powerControlActionKeyboard)(_powerControlTargetKeyboard, whatToDo); +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +void ApplePS2Controller::installPowerControlAction( + PS2DeviceType deviceType, + OSObject * target, + PS2PowerControlAction action ) +{ + if ( deviceType == kDT_Keyboard && _powerControlInstalledKeyboard == false ) + { + target->retain(); + _powerControlTargetKeyboard = target; + _powerControlActionKeyboard = action; + _powerControlInstalledKeyboard = true; + } + else if ( deviceType == kDT_Mouse && _powerControlInstalledMouse == false ) + { + target->retain(); + _powerControlTargetMouse = target; + _powerControlActionMouse = action; + _powerControlInstalledMouse = true; + } +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +void ApplePS2Controller::uninstallPowerControlAction( PS2DeviceType deviceType ) +{ + if ( deviceType == kDT_Keyboard && _powerControlInstalledKeyboard == true ) + { + _powerControlInstalledKeyboard = false; + _powerControlActionKeyboard = NULL; + _powerControlTargetKeyboard->release(); + _powerControlTargetKeyboard = 0; + } + else if ( deviceType == kDT_Mouse && _powerControlInstalledMouse == true ) + { + _powerControlInstalledMouse = false; + _powerControlActionMouse = NULL; + _powerControlTargetMouse->release(); + _powerControlTargetMouse = 0; + } +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +void ApplePS2Controller::installMessageAction( + PS2DeviceType deviceType, + OSObject *target, PS2MessageAction action) +{ + if (deviceType == kDT_Keyboard && !_messageInstalledKeyboard) + { + target->retain(); + _messageTargetKeyboard = target; + _messageActionKeyboard = action; + _messageInstalledKeyboard = true; + } + else if (deviceType == kDT_Mouse && !_messageInstalledMouse) + { + target->retain(); + _messageTargetMouse = target; + _messageActionMouse = action; + _messageInstalledMouse = true; + } +} + +void ApplePS2Controller::uninstallMessageAction(PS2DeviceType deviceType) +{ + if (deviceType == kDT_Keyboard && _messageInstalledKeyboard) + { + _messageInstalledKeyboard = false; + _messageActionKeyboard = NULL; + _messageTargetKeyboard->release(); + _messageTargetKeyboard = NULL; + } + else if (deviceType == kDT_Mouse && _messageInstalledMouse) + { + _messageInstalledMouse = false; + _messageActionMouse = NULL; + _messageTargetMouse->release(); + _messageTargetMouse = NULL; + } +} + +void ApplePS2Controller::dispatchMessage(PS2DeviceType deviceType, int message, void* data) +{ + if (deviceType == kDT_Keyboard && _messageInstalledKeyboard) + { + (*_messageActionKeyboard)(_messageTargetKeyboard, message, data); + } + else if (deviceType == kDT_Mouse && _messageInstalledMouse) + { + (*_messageActionMouse)(_messageTargetMouse, message, data); + } +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +void ApplePS2Controller::lock() +{ + assert(_cmdbyteLock); + IOLockLock(_cmdbyteLock); +} + +void ApplePS2Controller::unlock() +{ + assert(_cmdbyteLock); + IOLockUnlock(_cmdbyteLock); +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +#define kDefault "Default" + +struct DSDT_HEADER +{ + uint32_t tableSignature; + uint32_t tableLength; + uint8_t specCompliance; + uint8_t checkSum; + char oemID[6]; + char oemTableID[8]; + uint32_t oemRevision; + uint32_t creatorID; + uint32_t creatorRevision; +}; + +#define DSDT_SIGNATURE ('D' | 'S'<<8 | 'D'<<16 | 'T'<<24) + +static const DSDT_HEADER* getDSDT() +{ + IORegistryEntry* reg = IORegistryEntry::fromPath("IOService:/AppleACPIPlatformExpert"); + if (!reg) + return NULL; + OSDictionary* dict = OSDynamicCast(OSDictionary, reg->getProperty("ACPI Tables")); + reg->release(); + if (!dict) + return NULL; + OSData* data = OSDynamicCast(OSData, dict->getObject("DSDT")); + if (!data || data->getLength() < sizeof(DSDT_HEADER)) + return NULL; + const DSDT_HEADER* pDSDT = (const DSDT_HEADER*)data->getBytesNoCopy(); + if (!pDSDT || data->getLength() < sizeof(DSDT_HEADER) || pDSDT->tableSignature != DSDT_SIGNATURE) + return NULL; + return pDSDT; +} + +static void stripTrailingSpaces(char* str) +{ + char* p = str; + for (; *p; p++) + ; + for (--p; p >= str && *p == ' '; --p) + *p = 0; +} + +static OSString* getPlatformManufacturer() +{ + // allow override in PS2K ACPI device + IORegistryEntry* reg = IORegistryEntry::fromPath("IOService:/AppleACPIPlatformExpert/PS2K"); + if (reg) { + OSString* id = OSDynamicCast(OSString, reg->getProperty("RM,oem-id")); + reg->release(); + if (id) + return id; + } + // otherwise use DSDT header + const DSDT_HEADER* pDSDT = getDSDT(); + if (!pDSDT) + return NULL; + // copy to static data, NUL terminate, strip trailing spaces, and return + static char oemID[sizeof(pDSDT->oemID)+1]; + bcopy(pDSDT->oemID, oemID, sizeof(pDSDT->oemID)); + oemID[sizeof(oemID)-1] = 0; + stripTrailingSpaces(oemID); + return OSString::withCStringNoCopy(oemID); +} + +static OSString* getPlatformProduct() +{ + // allow override in PS2K ACPI device + IORegistryEntry* reg = IORegistryEntry::fromPath("IOService:/AppleACPIPlatformExpert/PS2K"); + if (reg) { + OSString* id = OSDynamicCast(OSString, reg->getProperty("RM,oem-table-id")); + reg->release(); + if (id) + return id; + } + const DSDT_HEADER* pDSDT = getDSDT(); + if (!pDSDT) + return NULL; + // copy to static data, NUL terminate, strip trailing spaces, and return + static char oemTableID[sizeof(pDSDT->oemTableID)+1]; + bcopy(pDSDT->oemTableID, oemTableID, sizeof(pDSDT->oemTableID)); + oemTableID[sizeof(oemTableID)-1] = 0; + stripTrailingSpaces(oemTableID); + return OSString::withCStringNoCopy(oemTableID); +} + +static OSDictionary* _getConfigurationNode(OSDictionary *root, const char *name); + +static OSDictionary* _getConfigurationNode(OSDictionary *root, OSString *name) +{ + OSDictionary *configuration = NULL; + + if (root && name) { + if (!(configuration = OSDynamicCast(OSDictionary, root->getObject(name)))) { + if (OSString *link = OSDynamicCast(OSString, root->getObject(name))) { + const char* p1 = link->getCStringNoCopy(); + const char* p2 = p1; + for (; *p2 && *p2 != ';'; ++p2); + if (*p2 != ';') { + configuration = _getConfigurationNode(root, link); + } + else { + if (OSString* strip = OSString::withString(link)) { + strip->setChar(0, (unsigned)(p2 - p1)); + configuration = _getConfigurationNode(root, strip); + strip->release(); + } + } + } + } + } + + return configuration; +} + +static OSDictionary* _getConfigurationNode(OSDictionary *root, const char *name) +{ + OSDictionary *configuration = NULL; + + if (root && name) { + OSString *nameNode = OSString::withCStringNoCopy(name); + + configuration = _getConfigurationNode(root, nameNode); + + OSSafeReleaseNULL(nameNode); + } + + return configuration; +} + +OSDictionary* ApplePS2Controller::getConfigurationNode(OSDictionary* list, OSString *model) +{ + OSDictionary *configuration = NULL; + + if (OSString *manufacturer = getPlatformManufacturer()) + if (OSDictionary *manufacturerNode = OSDynamicCast(OSDictionary, list->getObject(manufacturer))) + if (!(configuration = _getConfigurationNode(manufacturerNode, getPlatformProduct()))) + if (!(configuration = _getConfigurationNode(manufacturerNode, model))) + configuration = _getConfigurationNode(manufacturerNode, kDefault); + + if (!configuration && !(configuration = _getConfigurationNode(list, model))) + configuration = _getConfigurationNode(list, kDefault); + + return configuration; +} + +EXPORT OSDictionary* ApplePS2Controller::makeConfigurationNode(OSDictionary* list, OSString* model) +{ + if (!list) + return NULL; + + OSDictionary* result = 0; + OSDictionary* defaultNode = _getConfigurationNode(list, kDefault); + OSDictionary* platformNode = getConfigurationNode(list, model); + if (defaultNode) + { + // have default node, result is merge with platform node + result = OSDictionary::withDictionary(defaultNode); + if (result && platformNode) + result->merge(platformNode); + } + else if (platformNode) + { + // no default node, try to use just platform node + result = OSDictionary::withDictionary(platformNode); + } + return result; +} diff --git a/VoodooPS2Controller/VoodooPS2Controller.h b/VoodooPS2Controller/VoodooPS2Controller.h new file mode 100644 index 0000000..24e32f7 --- /dev/null +++ b/VoodooPS2Controller/VoodooPS2Controller.h @@ -0,0 +1,343 @@ +/* + * Copyright (c) 1998-2000 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * The contents of this file constitute Original Code as defined in and + * are subject to the Apple Public Source License Version 1.1 (the + * "License"). You may not use this file except in compliance with the + * License. Please obtain a copy of the License at + * http://www.apple.com/publicsource and read it before using this file. + * + * This Original Code and all software distributed under the License are + * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the + * License for the specific language governing rights and limitations + * under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +#ifndef _APPLEPS2CONTROLLER_H +#define _APPLEPS2CONTROLLER_H + +#include +#include +#include +#include "ApplePS2Device.h" + +class ApplePS2KeyboardDevice; +class ApplePS2MouseDevice; + +// +// This section describes the problem with the PS/2 controller design and what +// we are doing about it (OUT_OF_ORDER_DATA_CORRECTION_FEATURE). +// +// While the controller processes requests sent by the client drivers, at some +// point in most requests, a read needs to be made from the data port to check +// an acknowledge or receive some sort of data. We illustrate this issue with +// an example -- a write LEDs request to the keyboard: +// +// 1. Write Write LED command. +// 2. Read 0xFA Verify the acknowledge (0xFA). +// 3. Write Write LED state. +// 4. Read 0xFA Verify the acknowledge (0xFA). +// +// The problem is that the keyboard (when it is enabled) can send key events +// to the controller at any time, including when the controller is expecting +// to read an acknowledge next. What ends up happening is this sequence: +// +// a. Write Write LED command. +// b. Read 0x21 Keyboard reports [F] key was depressed, not realizing that +// we're still expecting a response to the command we JUST +// sent the keyboard. We receive 0x21 as a response to our +// command, and figure the command failed. +// c. Get 0xFA Keyboard NOW decides to respond to the command with an +// acknowledge. We're not waiting to read anything, so +// this byte gets dispatched to the driver's interrupt +// handler, which spews out an error message saying it +// wasn't expecting an acknowledge. +// +// What can we do about this? In the above case, we can take note of the fact +// that we are specifically looking for the 0xFA acknowledgement byte (through +// the information passed in the kPS2C_ReadAndCompare primitive). If we don't +// receive this byte next on the input data stream, we put the byte we did get +// aside for a moment, and give the keyboard (or mouse) a second chance to +// respond correctly. +// +// If we receive the 0xFA acknowledgement byte on the second read, that we +// assume that situation described above just happened. We transparently +// dispatch the first byte to the driver's interrupt handler, where it was +// meant to go, and return the second correct byte to the read-and-compare +// logic, where it was meant to go. Everyone wins. +// +// The only situation this feature cannot help is where a kPS2C_ReadDataPort +// primitive is issued in place of a kPS2C_ReadDataPortAndCompare primitive. +// This is necessary in some requests because the driver does not know what +// it is going to receive. This can be illustrated in the mouse get info +// command. +// +// 1. Write Prepare to write to mouse. +// 2. Write Write information command. +// 3. Read 0xFA Verify the acknowledge (0xFA). __-> mouse can report mouse +// 4. Read Get first information byte. __-> packet bytes in between +// 5. Read Get second information byte. __-> these reads +// 6. Rrad Get third information byte. +// +// Controller cannot build any defenses against this. It is suggested that the +// driver writer disable the mouse first, then send any dangerous commands, and +// re-enable the mouse when the command completes. +// +// Note that the OUT_OF_ORDER_DATA_CORRECTION_FEATURE can be turned off at +// compile time. Please see the readDataPort:expecting: method for more +// information about the assumptions necessary for this feature. +// + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +// Definitions +// + +// Enable debugger support (eg. mini-monitor). + +#define DEBUGGER_SUPPORT 0 + +// Enable dynamic "second chance" re-ordering of input stream data if a +// command response fails to match the expected byte. + +#define OUT_OF_ORDER_DATA_CORRECTION_FEATURE 1 + +// Enable handling of interrupt data in workloop instead of at interrupt +// time. This way is easier to debug. For production use, this should +// be zero, such that PS2 data is buffered at real interrupt time, and handled +// as packets later in the workloop. + +#define HANDLE_INTERRUPT_DATA_LATER 0 +#define WATCHDOG_TIMER 0 + +// Interrupt definitions. + +#define kIRQ_Keyboard 1 +#define kIRQ_Mouse 12 +#define kIPL_Keyboard 6 +#define kIPL_Mouse 3 + +// Port timings. + +#define kDataDelay 7 // usec to delay before data is valid + +// Ports used to control the PS/2 keyboard/mouse and read data from it. + +#define kDataPort 0x60 // keyboard data & cmds (read/write) +#define kCommandPort 0x64 // keybd status (read), command (write) + +// Bit definitions for kCommandPort read values (status). + +#define kOutputReady 0x01 // output (from keybd) buffer full +#define kInputBusy 0x02 // input (to keybd) buffer full +#define kSystemFlag 0x04 // "System Flag" +#define kCommandLastSent 0x08 // 1 = cmd, 0 = data last sent +#define kKeyboardInhibited 0x10 // 0 if keyboard inhibited +#define kMouseData 0x20 // mouse data available + +// Watchdog timer definitions + +#define kWatchdogTimerInterval 100 + +#if DEBUGGER_SUPPORT +// Definitions for our internal keyboard queue (holds keys processed by the +// interrupt-time mini-monitor-key-sequence detection code). + +#define kKeyboardQueueSize 32 // number of KeyboardQueueElements + +typedef struct KeyboardQueueElement KeyboardQueueElement; +struct KeyboardQueueElement +{ + queue_chain_t chain; + UInt8 data; +}; +#endif //DEBUGGER_SUPPORT + +// Info.plist definitions + +#define kDisableDevice "DisableDevice" +#define kPlatformProfile "Platform Profile" + +#ifdef DEBUG +#define kMergedConfiguration "Merged Configuration" +#endif + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +// ApplePS2Controller Class Declaration +// + +class EXPORT ApplePS2Controller : public IOService +{ + typedef IOService super; + OSDeclareDefaultStructors(ApplePS2Controller); + +public: // interrupt-time variables and functions + IOInterruptEventSource * _interruptSourceKeyboard; + IOInterruptEventSource * _interruptSourceMouse; + IOInterruptEventSource * _interruptSourceQueue; + +#if DEBUGGER_SUPPORT + bool _debuggingEnabled; + + void lockController(int * state); + void unlockController(int state); + + bool doEscape(UInt8 key); + bool dequeueKeyboardData(UInt8 * key); + void enqueueKeyboardData(UInt8 key); +#endif //DEBUGGER_SUPPORT + +private: + IOWorkLoop * _workLoop; + queue_head_t _requestQueue; + IOLock* _requestQueueLock; + IOLock* _cmdbyteLock; + + OSObject * _interruptTargetKeyboard; + OSObject * _interruptTargetMouse; + PS2InterruptAction _interruptActionKeyboard; + PS2InterruptAction _interruptActionMouse; + PS2PacketAction _packetActionKeyboard; + PS2PacketAction _packetActionMouse; + bool _interruptInstalledKeyboard; + bool _interruptInstalledMouse; + + OSObject * _powerControlTargetKeyboard; + OSObject * _powerControlTargetMouse; + PS2PowerControlAction _powerControlActionKeyboard; + PS2PowerControlAction _powerControlActionMouse; + bool _powerControlInstalledKeyboard; + bool _powerControlInstalledMouse; + + int _ignoreInterrupts; + int _ignoreOutOfOrder; + + OSObject* _messageTargetKeyboard; + OSObject* _messageTargetMouse; + PS2MessageAction _messageActionKeyboard; + PS2MessageAction _messageActionMouse; + bool _messageInstalledKeyboard; + bool _messageInstalledMouse; + + ApplePS2MouseDevice * _mouseDevice; // mouse nub + ApplePS2KeyboardDevice * _keyboardDevice; // keyboard nub + +#if DEBUGGER_SUPPORT + IOSimpleLock * _controllerLock; // mach simple spin lock + + KeyboardQueueElement * _keyboardQueueAlloc; // queues' allocation space + queue_head_t _keyboardQueue; // queue of available keys + queue_head_t _keyboardQueueUnused; // queue of unused entries + + bool _extendedState; + UInt16 _modifierState; +#endif //DEBUGGER_SUPPORT + + thread_call_t _powerChangeThreadCall; + UInt32 _currentPowerState; + bool _hardwareOffline; + bool _suppressTimeout; +#ifdef NEWIRQ + bool _newIRQLayout; +#endif + int _wakedelay; + bool _mouseWakeFirst; + IOCommandGate* _cmdGate; +#if WATCHDOG_TIMER + IOTimerEventSource* _watchdogTimer; +#endif + + virtual PS2InterruptResult _dispatchDriverInterrupt(PS2DeviceType deviceType, UInt8 data); + virtual void dispatchDriverInterrupt(PS2DeviceType deviceType, UInt8 data); +#if HANDLE_INTERRUPT_DATA_LATER + virtual void interruptOccurred(IOInterruptEventSource *, int); +#else + void packetReadyMouse(IOInterruptEventSource*, int); + void packetReadyKeyboard(IOInterruptEventSource*, int); +#endif + void handleInterrupt(PS2DeviceType deviceType); +#if WATCHDOG_TIMER + void onWatchdogTimer(); +#endif + virtual void processRequest(PS2Request * request); + virtual void processRequestQueue(IOInterruptEventSource *, int); + + virtual UInt8 readDataPort(PS2DeviceType deviceType); + virtual void writeCommandPort(UInt8 byte); + virtual void writeDataPort(UInt8 byte); + void resetController(void); + + static void interruptHandlerMouse(OSObject*, void* refCon, IOService*, int); + static void interruptHandlerKeyboard(OSObject*, void* refCon, IOService*, int); + +#if OUT_OF_ORDER_DATA_CORRECTION_FEATURE + virtual UInt8 readDataPort(PS2DeviceType deviceType, UInt8 expectedByte); +#endif + + static void setPowerStateCallout(thread_call_param_t param0, + thread_call_param_t param1); + + static IOReturn setPowerStateAction(OSObject * target, + void * arg0, void * arg1, + void * arg2, void * arg3); + + virtual void setPowerStateGated(UInt32 newPowerState); + + virtual void dispatchDriverPowerControl(UInt32 whatToDo, PS2DeviceType deviceType); +#if DEBUGGER_SUPPORT + virtual void free(void); +#endif + IOReturn setPropertiesGated(OSObject* props); + void submitRequestAndBlockGated(PS2Request* request); + +public: + virtual bool init(OSDictionary * properties); + virtual bool start(IOService * provider); + virtual void stop(IOService * provider); + + virtual IOWorkLoop * getWorkLoop() const; + + virtual void installInterruptAction(PS2DeviceType deviceType, + OSObject * target, + PS2InterruptAction interruptAction, + PS2PacketAction packetAction); + virtual void uninstallInterruptAction(PS2DeviceType deviceType); + + virtual PS2Request* allocateRequest(int max = kMaxCommands); + virtual void freeRequest(PS2Request * request); + virtual bool submitRequest(PS2Request * request); + virtual void submitRequestAndBlock(PS2Request * request); + virtual UInt8 setCommandByte(UInt8 setBits, UInt8 clearBits); + void setCommandByteGated(PS2Request* request); + + virtual IOReturn setPowerState(unsigned long powerStateOrdinal, + IOService * policyMaker); + + virtual void installPowerControlAction(PS2DeviceType deviceType, + OSObject * target, + PS2PowerControlAction action); + + virtual void uninstallPowerControlAction(PS2DeviceType deviceType); + + virtual void installMessageAction(PS2DeviceType deviceType, + OSObject * target, + PS2MessageAction action); + + virtual void uninstallMessageAction(PS2DeviceType deviceType); + virtual void dispatchMessage(PS2DeviceType deviceType, int message, void* data); + + virtual IOReturn setProperties(OSObject* props); + virtual void lock(); + virtual void unlock(); + + static OSDictionary* getConfigurationNode(OSDictionary* list, OSString* model = 0); + static OSDictionary* makeConfigurationNode(OSDictionary* list, OSString* model = 0); +}; + +#endif /* _APPLEPS2CONTROLLER_H */ diff --git a/VoodooPS2Controller/en.lproj/InfoPlist.strings b/VoodooPS2Controller/en.lproj/InfoPlist.strings new file mode 100644 index 0000000..b92732c --- /dev/null +++ b/VoodooPS2Controller/en.lproj/InfoPlist.strings @@ -0,0 +1 @@ +/* Localized versions of Info.plist keys */ diff --git a/VoodooPS2Keyboard/ApplePS2ToADBMap.h b/VoodooPS2Keyboard/ApplePS2ToADBMap.h new file mode 100644 index 0000000..48afa07 --- /dev/null +++ b/VoodooPS2Keyboard/ApplePS2ToADBMap.h @@ -0,0 +1,1129 @@ +/* + * Copyright (c) 1998-2000 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * The contents of this file constitute Original Code as defined in and + * are subject to the Apple Public Source License Version 1.1 (the + * "License"). You may not use this file except in compliance with the + * License. Please obtain a copy of the License at + * http://www.apple.com/publicsource and read it before using this file. + * + * This Original Code and all software distributed under the License are + * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the + * License for the specific language governing rights and limitations + * under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +#ifndef _APPLEPS2TOADBMAP_H +#define _APPLEPS2TOADBMAP_H + +#define PROBOOK + +#define DEADKEY 0x80 +#define ADB_CONVERTER_LEN 256 * 2 // 0x00~0xff : normal key , 0x100~0x1ff : extended key +#define ADB_CONVERTER_EX_START 256 + +// PS/2 scancode reference : USB HID to PS/2 Scan Code Translation Table PS/2 Set 1 columns +// http://download.microsoft.com/download/1/6/1/161ba512-40e2-4cc9-843a-923143f3456c/translate.pdf +static const UInt8 PS2ToADBMapStock[ADB_CONVERTER_LEN] = +{ +/* ADB AT ANSI Key-Legend + ======================== */ + DEADKEY,// 00 + 0x35, // 01 Escape + 0x12, // 02 1! + 0x13, // 03 2@ + 0x14, // 04 3# + 0x15, // 05 4$ + 0x17, // 06 5% + 0x16, // 07 6^ + 0x1a, // 08 7& + 0x1c, // 09 8* + 0x19, // 0a 9( + 0x1d, // 0b 0) + 0x1b, // 0c -_ + 0x18, // 0d =+ + 0x33, // 0e Backspace + 0x30, // 0f Tab + 0x0c, // 10 qQ + 0x0d, // 11 wW + 0x0e, // 12 eE + 0x0f, // 13 rR + 0x11, // 14 tT + 0x10, // 15 yY + 0x20, // 16 uU + 0x22, // 17 iI + 0x1f, // 18 oO + 0x23, // 19 pP + 0x21, // 1a [{ + 0x1e, // 1b ]} + 0x24, // 1c Return + 0x3b, // 1d Left Control + 0x00, // 1e aA + 0x01, // 1f sS + 0x02, // 20 dD + 0x03, // 21 fF + 0x05, // 22 gG + 0x04, // 23 hH + 0x26, // 24 jJ + 0x28, // 25 kK + 0x25, // 26 lL + 0x29, // 27 ;: + 0x27, // 28 '" + 0x32, // 29 `~ + 0x38, // 2a Left Shift + 0x2a, // 2b \| , Europe 1(ISO) + 0x06, // 2c zZ + 0x07, // 2d xX + 0x08, // 2e cC + 0x09, // 2f vV + 0x0b, // 30 bB + 0x2d, // 31 nN + 0x2e, // 32 mM + 0x2b, // 33 ,< + 0x2f, // 34 .> + 0x2c, // 35 /? + 0x3c, // 36 Right Shift + 0x43, // 37 Keypad * + 0x3a, // 38 Left Alt + 0x31, // 39 Space + 0x39, // 3a Caps Lock + 0x7a, // 3b F1 + 0x78, // 3c F2 + 0x63, // 3d F3 + 0x76, // 3e F4 + 0x60, // 3f F5 + 0x61, // 40 F6 + 0x62, // 41 F7 + 0x64, // 42 F8 + 0x65, // 43 F9 + 0x6d, // 44 F10 + 0x47, // 45 Num Lock + 0x6b, // 46 Scroll Lock + 0x59, // 47 Keypad 7 Home + 0x5b, // 48 Keypad 8 Up + 0x5c, // 49 Keypad 9 PageUp + 0x4e, // 4a Keypad - + 0x56, // 4b Keypad 4 Left + 0x57, // 4c Keypad 5 + 0x58, // 4d Keypad 6 Right + 0x45, // 4e Keypad + + 0x53, // 4f Keypad 1 End + 0x54, // 50 Keypad 2 Down + 0x55, // 51 Keypad 3 PageDn + 0x52, // 52 Keypad 0 Insert + 0x41, // 53 Keypad . Delete + 0x44, // 54 SysReq + 0x46, // 55 + 0x0a, // 56 Europe 2(ISO) + 0x67, // 57 F11 + 0x6f, // 58 F12 + 0x51, // 59 Keypad = + DEADKEY,// 5a + DEADKEY,// 5b + 0x5f, // 5c Keyboard Int'l 6 (PC9800 Keypad , ) + DEADKEY,// 5d + DEADKEY,// 5e + DEADKEY,// 5f + DEADKEY,// 60 + DEADKEY,// 61 + DEADKEY,// 62 + DEADKEY,// 63 + 0x69, // 64 F13 + 0x6b, // 65 F14 + 0x71, // 66 F15 + 0x6a, // 67 F16 + 0x40, // 68 F17 + 0x4f, // 69 F18 + 0x50, // 6a F19 + 0x5a, // 6b F20 + DEADKEY,// 6c F21 + DEADKEY,// 6d F22 + DEADKEY,// 6e F23 + DEADKEY,// 6f + 0x68, // 70 Keyboard Intl'2 (Japanese Katakana/Hiragana) + DEADKEY,// 71 + DEADKEY,// 72 + 0x5e, // 73 Keyboard Int'l 1 (Japanese Ro) + DEADKEY,// 74 + DEADKEY,// 75 + DEADKEY,// 76 F24 , Keyboard Lang 5 (Japanese Zenkaku/Hankaku) + 0x68, // 77 Keyboard Lang 4 (Japanese Hiragana) + 0x68, // 78 Keyboard Lang 3 (Japanese Katakana) + 0x68, // 79 Keyboard Int'l 4 (Japanese Henkan) + DEADKEY,// 7a + 0x66, // 7b Keyboard Int'l 5 (Japanese Muhenkan) + DEADKEY,// 7c + 0x5d, // 7d Keyboard Int'l 3 (Japanese Yen) + 0x5f, // 7e Keypad , (Brazilian Keypad .) + DEADKEY,// 7f + DEADKEY,// 80 + DEADKEY,// 81 + DEADKEY,// 82 + DEADKEY,// 83 + DEADKEY,// 84 + DEADKEY,// 85 + DEADKEY,// 86 + DEADKEY,// 87 + DEADKEY,// 88 + DEADKEY,// 89 + DEADKEY,// 8a + DEADKEY,// 8b + DEADKEY,// 8c + DEADKEY,// 8d + DEADKEY,// 8e + DEADKEY,// 8f + DEADKEY,// 90 + DEADKEY,// 91 + DEADKEY,// 92 + DEADKEY,// 93 + DEADKEY,// 94 + DEADKEY,// 95 + DEADKEY,// 96 + DEADKEY,// 97 + DEADKEY,// 98 + DEADKEY,// 99 + DEADKEY,// 9a + DEADKEY,// 9b + DEADKEY,// 9c + DEADKEY,// 9d + DEADKEY,// 9e + DEADKEY,// 9f + DEADKEY,// a0 + DEADKEY,// a1 + DEADKEY,// a2 + DEADKEY,// a3 + DEADKEY,// a4 + DEADKEY,// a5 + DEADKEY,// a6 + DEADKEY,// a7 + DEADKEY,// a8 + DEADKEY,// a9 + DEADKEY,// aa + DEADKEY,// ab + DEADKEY,// ac + DEADKEY,// ad + DEADKEY,// ae + DEADKEY,// af + DEADKEY,// b0 + DEADKEY,// b1 + DEADKEY,// b2 + DEADKEY,// b3 + DEADKEY,// b4 + DEADKEY,// b5 + DEADKEY,// b6 + DEADKEY,// b7 + DEADKEY,// b8 + DEADKEY,// b9 + DEADKEY,// ba + DEADKEY,// bb + DEADKEY,// bc + DEADKEY,// bd + DEADKEY,// be + DEADKEY,// bf + DEADKEY,// c0 + DEADKEY,// c1 + DEADKEY,// c2 + DEADKEY,// c3 + DEADKEY,// c4 + DEADKEY,// c5 + DEADKEY,// c6 + DEADKEY,// c7 + DEADKEY,// c8 + DEADKEY,// c9 + DEADKEY,// ca + DEADKEY,// cb + DEADKEY,// cc + DEADKEY,// cd + DEADKEY,// ce + DEADKEY,// cf + DEADKEY,// d0 + DEADKEY,// d1 + DEADKEY,// d2 + DEADKEY,// d3 + DEADKEY,// d4 + DEADKEY,// d5 + DEADKEY,// d6 + DEADKEY,// d7 + DEADKEY,// d8 + DEADKEY,// d9 + DEADKEY,// da + DEADKEY,// db + DEADKEY,// dc + DEADKEY,// dd + DEADKEY,// de + DEADKEY,// df + DEADKEY,// e0 + DEADKEY,// e1 + DEADKEY,// e2 + DEADKEY,// e3 + DEADKEY,// e4 + DEADKEY,// e5 + DEADKEY,// e6 + DEADKEY,// e7 + DEADKEY,// e8 + DEADKEY,// e9 + DEADKEY,// ea + DEADKEY,// eb + DEADKEY,// ec + DEADKEY,// ed + DEADKEY,// ee + DEADKEY,// ef + DEADKEY,// f0 + 0x66, // f1* Keyboard Lang 2 (Korean Hanja) + 0x68, // f2* Keyboard Lang 1 (Korean Hangul) + DEADKEY,// f3 + DEADKEY,// f4 + DEADKEY,// f5 + DEADKEY,// f6 + DEADKEY,// f7 + DEADKEY,// f8 + DEADKEY,// f9 + DEADKEY,// fa + DEADKEY,// fb + DEADKEY,// fc + DEADKEY,// fd + DEADKEY,// fe + DEADKEY,// ff + DEADKEY,// e0 00 + DEADKEY,// e0 01 + DEADKEY,// e0 02 + DEADKEY,// e0 03 + DEADKEY,// e0 04 + 0x91, // e0 05 dell down + 0x90, // e0 06 dell up + DEADKEY,// e0 07 +#ifndef PROBOOK + 0x90, // e0 08 samsung up + 0x91, // e0 09 samsung down +#else + DEADKEY,// e0 08 + 0x83, // e0 09 Launchpad (hp Fn+F6) +#endif + 0xa0, // e0 0a Mission Control (hp Fn+F5) + DEADKEY,// e0 0b + DEADKEY,// e0 0c + DEADKEY,// e0 0d + DEADKEY,// e0 0e + DEADKEY,// e0 0f + 0x4d, // e0 10 Scan Previous Track (hp Fn+F10) + DEADKEY,// e0 11 + 0x91, // e0 12 hp down (Fn+F2) + DEADKEY,// e0 13 + DEADKEY,// e0 14 + DEADKEY,// e0 15 + DEADKEY,// e0 16 + 0x90, // e0 17 hp up (Fn+F3) + DEADKEY,// e0 18 + 0x42, // e0 19 Scan Next Track (hp Fn+F12) + DEADKEY,// e0 1a + DEADKEY,// e0 1b + 0x4c, // e0 1c Keypad Enter + 0x3e, // e0 1d Right Control + DEADKEY,// e0 1e + DEADKEY,// e0 1f + 0x4a, // e0 20 Mute (hp Fn+F7) + DEADKEY,// e0 21 Calculator + 0x34, // e0 22 Play/Pause (hp Fn+F11) + DEADKEY,// e0 23 + DEADKEY,// e0 24 Stop + DEADKEY,// e0 25 + DEADKEY,// e0 26 + DEADKEY,// e0 27 + DEADKEY,// e0 28 + DEADKEY,// e0 29 + DEADKEY,// e0 2a + DEADKEY,// e0 2b + DEADKEY,// e0 2c + DEADKEY,// e0 2d + 0x49, // e0 2e Volume Down (hp Fn+F8) + DEADKEY,// e0 2f + 0x48, // e0 30 Volume Up (hp Fn+F9) + DEADKEY,// e0 31 + DEADKEY,// e0 32 WWW Home + DEADKEY,// e0 33 + DEADKEY,// e0 34 + 0x4b, // e0 35 Keypad / + DEADKEY,// e0 36 + 0x69, // e0 37 Print Screen + 0x3d, // e0 38 Right Alt + DEADKEY,// e0 39 + DEADKEY,// e0 3a + DEADKEY,// e0 3b + DEADKEY,// e0 3c + DEADKEY,// e0 3d + DEADKEY,// e0 3e + DEADKEY,// e0 3f + DEADKEY,// e0 40 + DEADKEY,// e0 41 + DEADKEY,// e0 42 + DEADKEY,// e0 43 + DEADKEY,// e0 44 + 0x71, // e0 45* Pause + DEADKEY,// e0 46* Break(Ctrl-Pause) + 0x73, // e0 47 Home + 0x7e, // e0 48 Up Arrow + 0x74, // e0 49 Page Up + DEADKEY,// e0 4a + 0x7b, // e0 4b Left Arrow + DEADKEY,// e0 4c + 0x7c, // e0 4d Right Arrow + 0x90, // e0 4e acer up + 0x77, // e0 4f End + 0x7d, // e0 50 Down Arrow + 0x79, // e0 51 Page Down + 0x92, // e0 52 Insert = Eject + 0x75, // e0 53 Delete + DEADKEY,// e0 54 + DEADKEY,// e0 55 + DEADKEY,// e0 56 + DEADKEY,// e0 57 + DEADKEY,// e0 58 + 0x90, // e0 59 acer up for my acer + DEADKEY,// e0 5a + 0x37, // e0 5b Left GUI(Windows) + 0x36, // e0 5c Right GUI(Windows) + 0x6e, // e0 5d App( Windows context menu key ) + 0x7f, // e0 5e System Power / Keyboard Power + DEADKEY,// e0 5f System Sleep (hp Fn+F1) + DEADKEY,// e0 60 + DEADKEY,// e0 61 + DEADKEY,// e0 62 + DEADKEY,// e0 63 System Wake + DEADKEY,// e0 64 + DEADKEY,// e0 65 WWW Search + DEADKEY,// e0 66 WWW Favorites + DEADKEY,// e0 67 WWW Refresh + DEADKEY,// e0 68 WWW Stop + DEADKEY,// e0 69 WWW Forward + DEADKEY,// e0 6a WWW Back + DEADKEY,// e0 6b My Computer + DEADKEY,// e0 6c Mail + DEADKEY,// e0 6d Media Select +#ifndef PROBOOK + 0x90, // e0 6e acer up + 0x91, // e0 6f acer down +#else + 0x70, // e0 6e Video Mirror = hp Fn+F4 + DEADKEY,// e0 6f Fn+Home +#endif + DEADKEY,// e0 70 + DEADKEY,// e0 71 + DEADKEY,// e0 72 + DEADKEY,// e0 73 + DEADKEY,// e0 74 + DEADKEY,// e0 75 + DEADKEY,// e0 76 +#ifndef PROBOOK + 0x91, // e0 77 lg down + 0x90, // e0 78 lg up +#else + DEADKEY,// e0 77 + DEADKEY,// e0 78 WiFi on/off button on HP ProBook +#endif + DEADKEY,// e0 79 + DEADKEY,// e0 7a + DEADKEY,// e0 7b + DEADKEY,// e0 7c + DEADKEY,// e0 7d + DEADKEY,// e0 7e + DEADKEY,// e0 7f + DEADKEY,// e0 80 + DEADKEY,// e0 81 + DEADKEY,// e0 82 + DEADKEY,// e0 83 + DEADKEY,// e0 84 + DEADKEY,// e0 85 + DEADKEY,// e0 86 + DEADKEY,// e0 87 + DEADKEY,// e0 88 + DEADKEY,// e0 89 + DEADKEY,// e0 8a + DEADKEY,// e0 8b + DEADKEY,// e0 8c + DEADKEY,// e0 8d + DEADKEY,// e0 8e + DEADKEY,// e0 8f + DEADKEY,// e0 90 + DEADKEY,// e0 91 + DEADKEY,// e0 92 + DEADKEY,// e0 93 + DEADKEY,// e0 94 + DEADKEY,// e0 95 + DEADKEY,// e0 96 + DEADKEY,// e0 97 + DEADKEY,// e0 98 + DEADKEY,// e0 99 + DEADKEY,// e0 9a + DEADKEY,// e0 9b + DEADKEY,// e0 9c + DEADKEY,// e0 9d + DEADKEY,// e0 9e + DEADKEY,// e0 9f + DEADKEY,// e0 a0 + DEADKEY,// e0 a1 + DEADKEY,// e0 a2 + DEADKEY,// e0 a3 + DEADKEY,// e0 a4 + DEADKEY,// e0 a5 + DEADKEY,// e0 a6 + DEADKEY,// e0 a7 + DEADKEY,// e0 a8 + DEADKEY,// e0 a9 + DEADKEY,// e0 aa + DEADKEY,// e0 ab + DEADKEY,// e0 ac + DEADKEY,// e0 ad + DEADKEY,// e0 ae + DEADKEY,// e0 af + DEADKEY,// e0 b0 + DEADKEY,// e0 b1 + DEADKEY,// e0 b2 + DEADKEY,// e0 b3 + DEADKEY,// e0 b4 + DEADKEY,// e0 b5 + DEADKEY,// e0 b6 + DEADKEY,// e0 b7 + DEADKEY,// e0 b8 + DEADKEY,// e0 b9 + DEADKEY,// e0 ba + DEADKEY,// e0 bb + DEADKEY,// e0 bc + DEADKEY,// e0 bd + DEADKEY,// e0 be + DEADKEY,// e0 bf + DEADKEY,// e0 c0 + DEADKEY,// e0 c1 + DEADKEY,// e0 c2 + DEADKEY,// e0 c3 + DEADKEY,// e0 c4 + DEADKEY,// e0 c5 + DEADKEY,// e0 c6 + DEADKEY,// e0 c7 + DEADKEY,// e0 c8 + DEADKEY,// e0 c9 + DEADKEY,// e0 ca + DEADKEY,// e0 cb + DEADKEY,// e0 cc + DEADKEY,// e0 cd + DEADKEY,// e0 ce + DEADKEY,// e0 cf + DEADKEY,// e0 d0 + DEADKEY,// e0 d1 + DEADKEY,// e0 d2 + DEADKEY,// e0 d3 + DEADKEY,// e0 d4 + DEADKEY,// e0 d5 + DEADKEY,// e0 d6 + DEADKEY,// e0 d7 + DEADKEY,// e0 d8 + DEADKEY,// e0 d9 + DEADKEY,// e0 da + DEADKEY,// e0 db + DEADKEY,// e0 dc + DEADKEY,// e0 dd + DEADKEY,// e0 de + DEADKEY,// e0 df + DEADKEY,// e0 e0 + DEADKEY,// e0 e1 + DEADKEY,// e0 e2 + DEADKEY,// e0 e3 + DEADKEY,// e0 e4 + DEADKEY,// e0 e5 + DEADKEY,// e0 e6 + DEADKEY,// e0 e7 + DEADKEY,// e0 e8 + DEADKEY,// e0 e9 + DEADKEY,// e0 ea + DEADKEY,// e0 eb + DEADKEY,// e0 ec + DEADKEY,// e0 ed + DEADKEY,// e0 ee + DEADKEY,// e0 ef + DEADKEY,// e0 f0 // Note: codes e0f0 through e0ff are reserved for ACPI callback + DEADKEY,// e0 f1 + DEADKEY,// e0 f2 + DEADKEY,// e0 f3 + DEADKEY,// e0 f4 + DEADKEY,// e0 f5 + DEADKEY,// e0 f6 + DEADKEY,// e0 f7 + DEADKEY,// e0 f8 + DEADKEY,// e0 f9 + DEADKEY,// e0 fa + DEADKEY,// e0 fb + DEADKEY,// e0 fc + DEADKEY,// e0 fd + DEADKEY,// e0 fe + DEADKEY // e0 ff // End reserved +}; + +/////////////////////////////////////////////////////////////////////////////////// +// +// +// high-byte of flags are (bit number + 1) for modifier key tracking +// 1: left control +// 2: right control +// 3: left shift +// 4: right shift +// 5: left alt +// 6: right alt +// 7: left windows +// 8: right windows +// 9: left Fn (e0 63 on Lenovo u430) +// 10: windows context menu (usually on right) +// +// low-byte is used for other purposes +// bit 0: breakless bit (set by "PS2 Breakless" +// + +#define kMaskLeftControl 0x0001 +#define kMaskRightControl 0x0002 +#define kMaskLeftShift 0x0004 +#define kMaskRightShift 0x0008 +#define kMaskLeftAlt 0x0010 +#define kMaskRightAlt 0x0020 +#define kMaskLeftWindows 0x0040 +#define kMaskRightWindows 0x0080 +#define kMaskLeftFn 0x0100 +#define kMaskWindowsContext 0x0200 + +static const UInt16 _PS2flagsStock[ADB_CONVERTER_LEN] = +{ + // flags/modifier key AT ANSI Key-Legend + 0x00, // 00 + 0x00, // 01 Escape + 0x00, // 02 1! + 0x00, // 03 2@ + 0x00, // 04 3# + 0x00, // 05 4$ + 0x00, // 06 5% + 0x00, // 07 6^ + 0x00, // 08 7& + 0x00, // 09 8* + 0x00, // 0a 9( + 0x00, // 0b 0) + 0x00, // 0c -_ + 0x00, // 0d =+ + 0x00, // 0e Backspace + 0x00, // 0f Tab + 0x00, // 10 qQ + 0x00, // 11 wW + 0x00, // 12 eE + 0x00, // 13 rR + 0x00, // 14 tT + 0x00, // 15 yY + 0x00, // 16 uU + 0x00, // 17 iI + 0x00, // 18 oO + 0x00, // 19 pP + 0x00, // 1a [{ + 0x00, // 1b ]} + 0x00, // 1c Return + 0x0100, // 1d Left Control + 0x00, // 1e aA + 0x00, // 1f sS + 0x00, // 20 dD + 0x00, // 21 fF + 0x00, // 22 gG + 0x00, // 23 hH + 0x00, // 24 jJ + 0x00, // 25 kK + 0x00, // 26 lL + 0x00, // 27 ;: + 0x00, // 28 '" + 0x00, // 29 `~ + 0x0300, // 2a Left Shift + 0x00, // 2b \| , Europe 1(ISO) + 0x00, // 2c zZ + 0x00, // 2d xX + 0x00, // 2e cC + 0x00, // 2f vV + 0x00, // 30 bB + 0x00, // 31 nN + 0x00, // 32 mM + 0x00, // 33 ,< + 0x00, // 34 .> + 0x00, // 35 /? + 0x0400, // 36 Right Shift + 0x00, // 37 Keypad * + 0x0500, // 38 Left Alt + 0x00, // 39 Space + 0x00, // 3a Caps Lock + 0x00, // 3b F1 + 0x00, // 3c F2 + 0x00, // 3d F3 + 0x00, // 3e F4 + 0x00, // 3f F5 + 0x00, // 40 F6 + 0x00, // 41 F7 + 0x00, // 42 F8 + 0x00, // 43 F9 + 0x00, // 44 F10 + 0x00, // 45 Num Lock + 0x00, // 46 Scroll Lock + 0x00, // 47 Keypad 7 Home + 0x00, // 48 Keypad 8 Up + 0x00, // 49 Keypad 9 PageUp + 0x00, // 4a Keypad - + 0x00, // 4b Keypad 4 Left + 0x00, // 4c Keypad 5 + 0x00, // 4d Keypad 6 Right + 0x00, // 4e Keypad + + 0x00, // 4f Keypad 1 End + 0x00, // 50 Keypad 2 Down + 0x00, // 51 Keypad 3 PageDn + 0x00, // 52 Keypad 0 Insert + 0x00, // 53 Keypad . Delete + 0x00, // 54 SysReq + 0x00, // 55 + 0x00, // 56 Europe 2(ISO) + 0x00, // 57 F11 + 0x00, // 58 F12 + 0x00, // 59 Keypad = + 0x00, // 5a + 0x00, // 5b + 0x00, // 5c Keyboard Int'l 6 (PC9800 Keypad , ) + 0x00, // 5d + 0x00, // 5e + 0x00, // 5f + 0x00, // 60 + 0x00, // 61 + 0x00, // 62 + 0x00, // 63 + 0x00, // 64 F13 + 0x00, // 65 F14 + 0x00, // 66 F15 + 0x00, // 67 F16 + 0x00, // 68 F17 + 0x00, // 69 F18 + 0x00, // 6a F19 + 0x00, // 6b F20 + 0x00, // 6c F21 + 0x00, // 6d F22 + 0x00, // 6e F23 + 0x00, // 6f + 0x00, // 70 Keyboard Intl'2 (Japanese Katakana/Hiragana) + 0x00, // 71 + 0x00, // 72 + 0x00, // 73 Keyboard Int'l 1 (Japanese Ro) + 0x00, // 74 + 0x00, // 75 + 0x00, // 76 F24 , Keyboard Lang 5 (Japanese Zenkaku/Hankaku) + 0x00, // 77 Keyboard Lang 4 (Japanese Hiragana) + 0x00, // 78 Keyboard Lang 3 (Japanese Katakana) + 0x00, // 79 Keyboard Int'l 4 (Japanese Henkan) + 0x00, // 7a + 0x00, // 7b Keyboard Int'l 5 (Japanese Muhenkan) + 0x00, // 7c + 0x00, // 7d Keyboard Int'l 3 (Japanese Yen) + 0x00, // 7e Keypad , (Brazilian Keypad .) + 0x00, // 7f + 0x00, // 80 + 0x00, // 81 + 0x00, // 82 + 0x00, // 83 + 0x00, // 84 + 0x00, // 85 + 0x00, // 86 + 0x00, // 87 + 0x00, // 88 + 0x00, // 89 + 0x00, // 8a + 0x00, // 8b + 0x00, // 8c + 0x00, // 8d + 0x00, // 8e + 0x00, // 8f + 0x00, // 90 + 0x00, // 91 + 0x00, // 92 + 0x00, // 93 + 0x00, // 94 + 0x00, // 95 + 0x00, // 96 + 0x00, // 97 + 0x00, // 98 + 0x00, // 99 + 0x00, // 9a + 0x00, // 9b + 0x00, // 9c + 0x00, // 9d + 0x00, // 9e + 0x00, // 9f + 0x00, // a0 + 0x00, // a1 + 0x00, // a2 + 0x00, // a3 + 0x00, // a4 + 0x00, // a5 + 0x00, // a6 + 0x00, // a7 + 0x00, // a8 + 0x00, // a9 + 0x00, // aa + 0x00, // ab + 0x00, // ac + 0x00, // ad + 0x00, // ae + 0x00, // af + 0x00, // b0 + 0x00, // b1 + 0x00, // b2 + 0x00, // b3 + 0x00, // b4 + 0x00, // b5 + 0x00, // b6 + 0x00, // b7 + 0x00, // b8 + 0x00, // b9 + 0x00, // ba + 0x00, // bb + 0x00, // bc + 0x00, // bd + 0x00, // be + 0x00, // bf + 0x00, // c0 + 0x00, // c1 + 0x00, // c2 + 0x00, // c3 + 0x00, // c4 + 0x00, // c5 + 0x00, // c6 + 0x00, // c7 + 0x00, // c8 + 0x00, // c9 + 0x00, // ca + 0x00, // cb + 0x00, // cc + 0x00, // cd + 0x00, // ce + 0x00, // cf + 0x00, // d0 + 0x00, // d1 + 0x00, // d2 + 0x00, // d3 + 0x00, // d4 + 0x00, // d5 + 0x00, // d6 + 0x00, // d7 + 0x00, // d8 + 0x00, // d9 + 0x00, // da + 0x00, // db + 0x00, // dc + 0x00, // dd + 0x00, // de + 0x00, // df + 0x00, // e0 + 0x00, // e1 + 0x00, // e2 + 0x00, // e3 + 0x00, // e4 + 0x00, // e5 + 0x00, // e6 + 0x00, // e7 + 0x00, // e8 + 0x00, // e9 + 0x00, // ea + 0x00, // eb + 0x00, // ec + 0x00, // ed + 0x00, // ee + 0x00, // ef + 0x00, // f0 + 0x00, // f1* Keyboard Lang 2 (Korean Hanja) + 0x00, // f2* Keyboard Lang 1 (Korean Hangul) + 0x00, // f3 + 0x00, // f4 + 0x00, // f5 + 0x00, // f6 + 0x00, // f7 + 0x00, // f8 + 0x00, // f9 + 0x00, // fa + 0x00, // fb + 0x00, // fc + 0x00, // fd + 0x00, // fe + 0x00, // ff + 0x00, // e0 00 + 0x00, // e0 01 + 0x00, // e0 02 + 0x00, // e0 03 + 0x00, // e0 04 + 0x00, // e0 05 dell down + 0x00, // e0 06 dell up + 0x00, // e0 07 +#ifndef PROBOOK + 0x00, // e0 08 samsung up + 0x00, // e0 09 samsung down +#else + 0x00, // e0 08 + 0x00, // e0 09 Launchpad (hp Fn+F6) +#endif + 0x00, // e0 0a Mission Control (hp Fn+F5) + 0x00, // e0 0b + 0x00, // e0 0c + 0x00, // e0 0d + 0x00, // e0 0e + 0x00, // e0 0f + 0x00, // e0 10 Scan Previous Track (hp Fn+F10) + 0x00, // e0 11 + 0x00, // e0 12 hp down (Fn+F2) + 0x00, // e0 13 + 0x00, // e0 14 + 0x00, // e0 15 + 0x00, // e0 16 + 0x00, // e0 17 hp up (Fn+F3) + 0x00, // e0 18 + 0x00, // e0 19 Scan Next Track (hp Fn+F12) + 0x00, // e0 1a + 0x00, // e0 1b + 0x00, // e0 1c Keypad Enter + 0x0200, // e0 1d Right Control + 0x00, // e0 1e + 0x00, // e0 1f + 0x00, // e0 20 Mute (hp Fn+F7) + 0x00, // e0 21 Calculator + 0x00, // e0 22 Play/Pause (hp Fn+F11) + 0x00, // e0 23 + 0x00, // e0 24 Stop + 0x00, // e0 25 + 0x00, // e0 26 + 0x00, // e0 27 Fn+fkeys/fkeys toggle alternate (default Ctrl+e037) + 0x00, // e0 28 + 0x00, // e0 29 + 0x00, // e0 2a + 0x00, // e0 2b + 0x00, // e0 2c + 0x00, // e0 2d + 0x00, // e0 2e Volume Down (hp Fn+F8) + 0x00, // e0 2f + 0x00, // e0 30 Volume Up (hp Fn+F9) + 0x00, // e0 31 + 0x00, // e0 32 WWW Home + 0x00, // e0 33 + 0x00, // e0 34 + 0x00, // e0 35 Keypad / + 0x00, // e0 36 + 0x00, // e0 37 Print Screen + 0x0600, // e0 38 Right Alt + 0x00, // e0 39 + 0x00, // e0 3a + 0x00, // e0 3b + 0x00, // e0 3c + 0x00, // e0 3d + 0x00, // e0 3e + 0x00, // e0 3f + 0x00, // e0 40 + 0x00, // e0 41 + 0x00, // e0 42 + 0x00, // e0 43 + 0x00, // e0 44 + 0x00, // e0 45* Pause + 0x00, // e0 46* Break(Ctrl-Pause) + 0x00, // e0 47 Home + 0x00, // e0 48 Up Arrow + 0x00, // e0 49 Page Up + 0x00, // e0 4a + 0x00, // e0 4b Left Arrow + 0x00, // e0 4c + 0x00, // e0 4d Right Arrow + 0x00, // e0 4e acer up + 0x00, // e0 4f End + 0x00, // e0 50 Down Arrow + 0x00, // e0 51 Page Down + 0x00, // e0 52 Insert = Eject + 0x00, // e0 53 Delete + 0x00, // e0 54 + 0x00, // e0 55 + 0x00, // e0 56 + 0x00, // e0 57 + 0x00, // e0 58 + 0x00, // e0 59 acer up for my acer + 0x00, // e0 5a + 0x0700, // e0 5b Left GUI(Windows) + 0x0800, // e0 5c Right GUI(Windows) + 0x0a00, // e0 5d App( Windows context menu key ) + 0x00, // e0 5e System Power / Keyboard Power + 0x00, // e0 5f System Sleep (hp Fn+F1) + 0x00, // e0 60 + 0x00, // e0 61 + 0x00, // e0 62 + 0x0900, // e0 63 System Wake (Fn on Lenovo u430) + 0x00, // e0 64 + 0x00, // e0 65 WWW Search + 0x00, // e0 66 WWW Favorites + 0x00, // e0 67 WWW Refresh + 0x00, // e0 68 WWW Stop + 0x00, // e0 69 WWW Forward + 0x00, // e0 6a WWW Back + 0x00, // e0 6b My Computer + 0x00, // e0 6c Mail + 0x00, // e0 6d Media Select +#ifndef PROBOOK + 0x00, // e0 6e acer up + 0x00, // e0 6f acer down +#else + 0x00, // e0 6e Video Mirror = hp Fn+F4 + 0x00, // e0 6f Fn+Home +#endif + 0x00, // e0 70 + 0x00, // e0 71 + 0x00, // e0 72 + 0x00, // e0 73 + 0x00, // e0 74 + 0x00, // e0 75 + 0x00, // e0 76 +#ifndef PROBOOK + 0x00, // e0 77 lg down + 0x00, // e0 78 lg up +#else + 0x00, // e0 77 + 0x00, // e0 78 WiFi on/off button on HP ProBook +#endif + 0x00, // e0 79 + 0x00, // e0 7a + 0x00, // e0 7b + 0x00, // e0 7c + 0x00, // e0 7d + 0x00, // e0 7e + 0x00, // e0 7f + 0x00, // e0 80 + 0x00, // e0 81 + 0x00, // e0 82 + 0x00, // e0 83 + 0x00, // e0 84 + 0x00, // e0 85 + 0x00, // e0 86 + 0x00, // e0 87 + 0x00, // e0 88 + 0x00, // e0 89 + 0x00, // e0 8a + 0x00, // e0 8b + 0x00, // e0 8c + 0x00, // e0 8d + 0x00, // e0 8e + 0x00, // e0 8f + 0x00, // e0 90 + 0x00, // e0 91 + 0x00, // e0 92 + 0x00, // e0 93 + 0x00, // e0 94 + 0x00, // e0 95 + 0x00, // e0 96 + 0x00, // e0 97 + 0x00, // e0 98 + 0x00, // e0 99 + 0x00, // e0 9a + 0x00, // e0 9b + 0x00, // e0 9c + 0x00, // e0 9d + 0x00, // e0 9e + 0x00, // e0 9f + 0x00, // e0 a0 + 0x00, // e0 a1 + 0x00, // e0 a2 + 0x00, // e0 a3 + 0x00, // e0 a4 + 0x00, // e0 a5 + 0x00, // e0 a6 + 0x00, // e0 a7 + 0x00, // e0 a8 + 0x00, // e0 a9 + 0x00, // e0 aa + 0x00, // e0 ab + 0x00, // e0 ac + 0x00, // e0 ad + 0x00, // e0 ae + 0x00, // e0 af + 0x00, // e0 b0 + 0x00, // e0 b1 + 0x00, // e0 b2 + 0x00, // e0 b3 + 0x00, // e0 b4 + 0x00, // e0 b5 + 0x00, // e0 b6 + 0x00, // e0 b7 + 0x00, // e0 b8 + 0x00, // e0 b9 + 0x00, // e0 ba + 0x00, // e0 bb + 0x00, // e0 bc + 0x00, // e0 bd + 0x00, // e0 be + 0x00, // e0 bf + 0x00, // e0 c0 + 0x00, // e0 c1 + 0x00, // e0 c2 + 0x00, // e0 c3 + 0x00, // e0 c4 + 0x00, // e0 c5 + 0x00, // e0 c6 + 0x00, // e0 c7 + 0x00, // e0 c8 + 0x00, // e0 c9 + 0x00, // e0 ca + 0x00, // e0 cb + 0x00, // e0 cc + 0x00, // e0 cd + 0x00, // e0 ce + 0x00, // e0 cf + 0x00, // e0 d0 + 0x00, // e0 d1 + 0x00, // e0 d2 + 0x00, // e0 d3 + 0x00, // e0 d4 + 0x00, // e0 d5 + 0x00, // e0 d6 + 0x00, // e0 d7 + 0x00, // e0 d8 + 0x00, // e0 d9 + 0x00, // e0 da + 0x00, // e0 db + 0x00, // e0 dc + 0x00, // e0 dd + 0x00, // e0 de + 0x00, // e0 df + 0x00, // e0 e0 + 0x00, // e0 e1 + 0x00, // e0 e2 + 0x00, // e0 e3 + 0x00, // e0 e4 + 0x00, // e0 e5 + 0x00, // e0 e6 + 0x00, // e0 e7 + 0x00, // e0 e8 + 0x00, // e0 e9 + 0x00, // e0 ea + 0x00, // e0 eb + 0x00, // e0 ec + 0x00, // e0 ed + 0x00, // e0 ee + 0x00, // e0 ef + 0x00, // e0 f0 // Note: codes e0f0 through e0ff are reserved for ACPI callback + 0x00, // e0 f1 + 0x00, // e0 f2 + 0x00, // e0 f3 + 0x00, // e0 f4 + 0x00, // e0 f5 + 0x00, // e0 f6 + 0x00, // e0 f7 + 0x00, // e0 f8 + 0x00, // e0 f9 + 0x00, // e0 fa + 0x00, // e0 fb + 0x00, // e0 fc + 0x00, // e0 fd + 0x00, // e0 fe + 0x00, // e0 ff // End reserved +}; + +#endif /* !_APPLEPS2TOADBMAP_H */ diff --git a/VoodooPS2Keyboard/VoodooPS2Keyboard-Breakless-Info.plist b/VoodooPS2Keyboard/VoodooPS2Keyboard-Breakless-Info.plist new file mode 100644 index 0000000..35db569 --- /dev/null +++ b/VoodooPS2Keyboard/VoodooPS2Keyboard-Breakless-Info.plist @@ -0,0 +1,173 @@ + + + + + Source Code + https://github.com/RehabMan/OS-X-Voodoo-PS2-Controller + CFBundleGetInfoString + ${MODULE_VERSION}, Copyright Apple Computer, Inc. 2000-2003, RehabMan 2012-2013 + CFBundleExecutable + VoodooPS2Keyboard + CFBundleIdentifier + org.rehabman.voodoo.driver.PS2Keyboard + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + Voodoo PS/2 Keyboard + CFBundlePackageType + KEXT + CFBundleShortVersionString + ${MODULE_VERSION} + CFBundleSignature + ???? + CFBundleVersion + ${MODULE_VERSION} + IOKitPersonalities + + ApplePS2Keyboard + + CFBundleIdentifier + org.rehabman.voodoo.driver.PS2Keyboard + IOClass + ApplePS2Keyboard + IOProviderClass + ApplePS2KeyboardDevice + Platform Profile + + Default + + Make Application key into Apple Fn key + + Make Application key into right windows + + Make right modifier keys into Hangul and Hanja + + Swap capslock and left control + + Swap command and option + + Use ISO layout keyboard + + alt_handler_id + 3 + SleepPressTime + 3000 + Breakless PS2 + + ;Items must be strings in the form of breaklessscan (in hex) + e05f + e012 + e017 + e06e + e00a + e009 + e020 + e02e + e030 + e010 + e022 + e019 + + Function Keys Standard + + ;Items must be strings in the form of scanfrom=scanto (in hex) + ;The following 12 items map Fn+fkeys to Fn+fkeys + e05f=e05f + e012=e012 + e017=e017 + e06e=e06e + e00a=e00a + e009=e009 + e020=e020 + e02e=e02e + e030=e030 + e010=e010 + e022=e022 + e019=e019 + ;The following 12 items map fkeys to fkeys + 3b=3b + 3c=3c + 3d=3d + 3e=3e + 3f=3f + 40=40 + 41=41 + 42=42 + 43=43 + 44=44 + 57=57 + 58=58 + + Function Keys Special + + ;Items must be strings in the form of scanfrom=scanto (in hex) + ;The following 12 items map Fn+fkeys to fkeys + e05f=3b + e012=3c + e017=3d + e06e=3e + e00a=3f + e009=40 + e020=41 + e02e=42 + e030=43 + e010=44 + e022=57 + e019=58 + ;The following 12 items map fkeys to Fn+fkeys + 3b=e05f + 3c=e012 + 3d=e017 + 3e=e06e + 3f=e00a + 40=e009 + 41=e020 + 42=e02e + 43=e030 + 44=e010 + 57=e022 + 58=e019 + + Custom PS2 Map + + ;Items must be strings in the form of scanfrom=scanto (in hex) + + Custom ADB Map + + ;Items must be strings in the form of scanfrom=adbto (in hex) + + ActionSwipeUp + 3b d, 37 d, 7e d, 7e u, 37 u, 3b u + ActionSwipeDown + 3b d, 37 d, 7d d, 7d u, 37 u, 3b u + ActionSwipeLeft + 3b d, 37 d, 7b d, 7b u, 37 u, 3b u + ActionSwipeRight + 3b d, 37 d, 7c d, 7c u, 37 u, 3b u + LogScanCodes + 0 + + + + + OSBundleLibraries + + com.apple.iokit.IOHIDSystem + 1.1 + com.apple.kpi.bsd + 8.0.0 + com.apple.kpi.iokit + 8.0.0 + com.apple.kpi.libkern + 8.0.0 + com.apple.kpi.mach + 8.0.0 + com.apple.kpi.unsupported + 8.0.0 + org.rehabman.voodoo.driver.PS2Controller + 2.8.15 + + OSBundleRequired + Console + + diff --git a/VoodooPS2Keyboard/VoodooPS2Keyboard-Info.plist b/VoodooPS2Keyboard/VoodooPS2Keyboard-Info.plist new file mode 100644 index 0000000..4531881 --- /dev/null +++ b/VoodooPS2Keyboard/VoodooPS2Keyboard-Info.plist @@ -0,0 +1,721 @@ + + + + + CFBundleExecutable + VoodooPS2Keyboard + CFBundleGetInfoString + ${MODULE_VERSION}, Copyright Apple Computer, Inc. 2000-2003, RehabMan 2012-2013 + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + Voodoo PS/2 Keyboard + CFBundlePackageType + KEXT + CFBundleShortVersionString + ${MODULE_VERSION} + CFBundleSignature + ???? + CFBundleVersion + ${MODULE_VERSION} + IOKitPersonalities + + ApplePS2Keyboard + + CFBundleIdentifier + org.rehabman.voodoo.driver.PS2Keyboard + IOClass + ApplePS2Keyboard + IOProviderClass + ApplePS2KeyboardDevice + Platform Profile + + DELL + + Dell-Keys + + Breakless PS2 + + e005 + e006 + + Function Keys Special + + ;The following 12 items map Fn+fkeys to fkeys + e020=3b + e02e=3c + e030=3d + e022=3e + ;Fn+f5 macro + ;Fn+f6 macro + ;Fn+f7 macro + ;Fn+f8 macro + ;Fn+f9 macro + ;Fn+f10 no code + e005=57 + e006=58 + ;The following 12 items map fkeys to Fn+fkeys + 3b=e020 + 3c=e02e + 3d=e030 + 3e=e022 + ;Fn+f5 macro + ;Fn+f6 macro + ;Fn+f7 macro + ;Fn+f8 macro + ;Fn+f9 macro + ;Fn+f10 no code + 57=e005 + 58=e006 + + Function Keys Standard + + ;The following 12 items map Fn+fkeys to Fn+fkeys + e020=e020 + e02e=e02e + e030=e030 + e022=e022 + ;Fn+f5 macro + ;Fn+f6 macro + ;Fn+f7 macro + ;Fn+f8 macro + ;Fn+f9 macro + ;Fn+f10 no code + e005=e005 + e006=e006 + ;The following 12 items map fkeys to fkeys + 3b=3b + 3c=3c + 3d=3d + 3e=3e + 3f=3f + 40=40 + 41=41 + 42=42 + 43=43 + 44=44 + 57=57 + 58=58 + + + HSW-LPT + Dell-Keys + SNB-CPT + + ActionSwipeDown + 63 d, 63 u + ActionSwipeUp + 61 d, 61 u + Breakless PS2 + + e01e;Touchpad Fn+f3 is breakless + e06e;REVIEW: temporary for case that macro inversion does not work... + + Custom ADB Map + + e009=83;Dell Support to Launchpad + e0f1=90;Call brightens up w RKA1 for special mode + e0f2=91;Call brightens down w RKA2 for special mode + e06e=70;Map vidmirror key for special mode default is adb90 + + Custom PS2 Map + + e01e=e037;Map tp disable to Fn+f3 + e037=e01e;Prevent PrntScr from triggering tp disable + + Function Keys Special + + ;The following 12 items map Fn+fkeys to fkeys + e06e=3b + e008=3c + e01e=3d + e005=3e + e006=3f + e00c=40 + ;Fn+f7 no dedicated macro + e010=42 + e022=43 + e019=44 + e02e=57 + e030=58 + ;The following 12 items map fkeys to Fn+fkeys + 3b=e06e;Map vidmirror key to f1 + 3c=e0f0;Map radio toggle action from EC query to f2 + 3d=e037;Map touchpad toggle button to f3 + 3e=e0f2;Map acpi RKA2 to f4 brightness down + 3f=e0f1;Map acpi RKA1 to f5 brightness up + 40=e0f3;Map acpi RKA3 to f6 keyboard backlight + ;Fn+f7 no macro + 42=e010 + 43=e022 + 44=e019 + 57=e02e + 58=e030 + + Function Keys Standard + + ;The following 12 items map Fn+fkeys to Fn+fkeys + e06e=e06e;Fn+f1 macro translated + e008=e008;Fn+f2 regular scancode and EC query call q8c + e01e=e037;Fn+f3 regular scancode and EC controls LED + e005=e005;Fn+f4 no ps2scancode and EC query call q81 + e006=e006;Fn+f5 no ps2scancode and EC query call q80 + e00c=e00c;Fn+f6 no ps2scancode and EC query call q8a + ;Fn+f7 no macro just regular f key + e010=e010; Fn+f8 regular scancode + e022=e022; Fn+f9 regular scancode + e019=e019;Fn+f10 regular scancode + e02e=e02e;Fn+f11 regular scancode + e030=e030;Fn+f12 regular scancode + ;Fn+f13 is mute dedicated button that always produces e020 regardless of Fn + ;The following 12 items map fkeys to fkeys + 3b=3b + 3c=3c + 3d=3d + 3e=3e + 3f=3f + 40=40 + 41=41 + 42=42 + 43=43 + 44=44 + 57=57 + 58=58 + + Macro Inversion + + ;This section maps ps2 codes (packet format) received quickly (macros) into fake ps2 codes (packet format) + ;Fn+F1 + + //8CbgAAAAACWwEZ + + + //8C7gAAAAAC2wGZ + + + //8C7gAAAAABmQLb + + + MaximumMacroTime + 35000000 + Note-Author + TimeWalker aka TimeWalker75a + Note-Comment + Keyboard Profile for DELL SandyBridge SecureCore Tiano based laptops (Vostro 3450 & 3750, Inspiron N4110, XPS L502x & L702x & L511z) + + WN09 + + Breakless PS2 + + e01b + e008 + e01e + e005 + e06e + e006 + + Custom ADB Map + + e01b=70 + e06e=83 + e005=6b + e006=71 + + Custom PS2 Map + + 56=2b + 29=56 + 2b=29 + e01e=e037 + e037=e01e + + + + Default + + ActionSwipeDown + 3e d, 7d d, 7d u, 3e u + ActionSwipeLeft + 3b d, 7c d, 7c u, 3b u + ActionSwipeRight + 3b d, 7b d, 7b u, 3b u + ActionSwipeUp + 3b d, 7e d, 7e u, 3b u + Breakless PS2 + + ;Items must be strings in the form of breaklessscan (in hex) + + Custom ADB Map + + ;Items must be strings in the form of scanfrom=adbto (in hex) + + Custom PS2 Map + + ;Items must be strings in the form of scanfrom=scanto (in hex) + e027=0;disable discrete fnkeys toggle + e028=0;disable discrete trackpad toggle + + HIDF12EjectDelay + 250 + LogScanCodes + 0 + Make Application key into Apple Fn key + + Make Application key into right windows + + Make right modifier keys into Hangul and Hanja + + SleepPressTime + 0 + Swap capslock and left control + + Swap command and option + + Use ISO layout keyboard + + alt_handler_id + 3 + + HPQOEM + + 1411 + ProBook-102;ProBook 4520s + 1619 + ProBook-87;ProBook 6560b + 161C + ProBook-87;ProBook 8460p + 164F + ProBook-87;ProBook 5330m + 167C + ProBook-102;ProBook 4530s + 167E + ProBook-102;ProBook 4330s + 1680 + ProBook-102;ProBook 4230s + 179B + ProBook-87;ProBook 6470b + 179C + ProBook-87;ProBook 6470b + 17A9 + ProBook-87;ProBook 8570b + 17F0 + ProBook-102;ProBook 4340s + 17F3 + ProBook-102;ProBook 4440s + 17F6 + ProBook-102;ProBook 4540s + 1942 + ProBook-87;ProBook 450s G1 + 1949 + ProBook-87;ProBook 450s G1 + 1962 + Haswell-Envy;HP Envy 15-j063cl + 1963 + Haswell-Envy;HP Envy 15-j063cl + 1965 + Haswell-Envy;HP Envy 17t-j100 + 1966 + Haswell-Envy;HP Envy 17t-j000 + 198F + ProBook-87;ProBook 450s G0 + Haswell-Envy + + BrightnessHack + + Custom ADB Map + + e019=42;next + e010=4d;previous + + Custom PS2 Map + + e045=e037 + e0ab=0;bogus Fn+F2/F3 + + + ProBook-102 + + Function Keys Special + + ;The following 12 items map Fn+fkeys to fkeys + e05f=3b + e012=3c + e017=3d + e06e=3e + e00a=3f + e009=40 + e020=41 + e02e=42 + e030=43 + e010=44 + e022=57 + e019=58 + ;The following 12 items map fkeys to Fn+fkeys + 3b=e05f + 3c=e012 + 3d=e017 + 3e=e06e + 3f=e00a + 40=e009 + 41=e020 + 42=e02e + 43=e030 + 44=e010 + 57=e022 + 58=e019 + + Function Keys Standard + + ;The following 12 items map Fn+fkeys to Fn+fkeys + e05f=e05f + e012=e012 + e017=e017 + e06e=e06e + e00a=e00a + e009=e009 + e020=e020 + e02e=e02e + e030=e030 + e010=e010 + e022=e022 + e019=e019 + ;The following 12 items map fkeys to fkeys + 3b=3b + 3c=3c + 3d=3d + 3e=3e + 3f=3f + 40=40 + 41=41 + 42=42 + 43=43 + 44=44 + 57=57 + 58=58 + + SleepPressTime + 3000 + + ProBook-87 + + Custom ADB Map + + 46=4d;scroll => Previous-track + e045=34;pause => Play-Pause + e052=42;insert => Next-track + e046=92;break => Eject + + Function Keys Special + + ;The following 8 items map Fn+fkeys to fkeys + e05f=3d + e06e=3e + e02e=40 + e030=41 + e009=42 + e012=43 + e017=44 + e033=57 + ;The following 8 items map fkeys to Fn+fkeys + 3d=e05f + 3e=e06e + 40=e02e + 41=e030 + 42=e037 + 43=e012 + 44=e017 + + Function Keys Standard + + ;The following 8 items map Fn+fkeys to Fn+fkeys + e05f=e05f + e06e=e06e + e02e=e02e + e030=e030 + e009=e009 + e012=e012 + e017=e017 + e033=e033 + ;The following 8 items map fkeys to fkeys + 3d=3d + 3e=3e + 40=40 + 41=41 + 42=42 + 43=43 + 44=44 + + SleepPressTime + 3000 + + + Intel + + CALPELLA + SamsungKeys + SamsungKeys + + Breakless PS2 + + e003 + e002 + e004 + e020 + ;e031 + e033 + e006 + e077 + e079 + e008 + e009 + + Custom ADB Map + + e002=70 + e006=80 + e008=90 + e009=91 + + Function Keys Special + + ;The following 12 items map Fn+fkeys to fkeys + ;fn+f1 no code + e003=3c + ;fn+f3 weird code + e002=3e + e004=3f + e020=40 + e031=41 + e033=42 + e006=43 + ;fn+f10 weird code + ;fn+f11 no code + ;fn+f12 scrolllock + ;The following 12 items map fkeys to Fn+fkeys + ;fn+f1 no code + 3c=e003 + ;fn+f3 weird code + 3e=e002 + 3f=e004 + 40=e020 + 41=e031 + 42=e033 + 43=e006 + ;fn+f10 weird code + ;fn+f11 no code + ;fn+f12 scrolllock + + Function Keys Standard + + ;The following 12 items map Fn+fkeys to Fn+fkeys + ;fn+f1 no code + e003=e003 + ;fn+f3 weird code + e002=e002 + e004=e004 + e020=e020 + e031=e031 + e033=e033 + e006=e006 + ;fn+f10 weird code + ;fn+f11 no code + ;fn+f12 scrolllock + ;The following 12 items map fkeys to fkeys + 3b=3b + 3c=3c + 3d=3d + 3e=3e + 3f=3f + 40=40 + 41=41 + 42=42 + 43=43 + 44=44 + 57=57 + 58=58 + + + + LENOVO + + Haswell-Ideapad + + Breakless PS2 + + e064 + e065 + e068 + e06a + e027 + + Custom ADB Map + + e063=3f;Apple Fn + e064=6b;F14 + e065=71;F15 + e068=4f;F18 + e0f2=65;special F9 + e0fb=91;brightness down + e0fc=90;brightness up + e06a=70;video mirror + + Custom PS2 Map + + e037=64;PrtSc=F13 + + Function Keys Special + + ;The following 12 items map Fn+fkeys to Fn+fkeys + e020=e020 + e02e=e02e + e030=e030 + e064=e064 + e065=e065 + e066=e028 + e067=e067 + e068=e068 + e069=e0f0 + e06a=e06a + e06b=e0fb + e06c=e0fc + ;The following 12 items map fkeys to fkeys + 3b=3b + 3c=3c + 3d=3d + 3e=3e + 3f=3f + 40=40 + 41=41 + 42=42 + 43=43 + 44=44 + 57=57 + 58=58 + + Function Keys Standard + + ;The following 12 items map Fn+fkeys to fkeys + e020=3b + e02e=3c + e030=3d + e064=3e + e065=3f + e066=40 + e067=41 + e068=42 + e069=e0f2 + e06a=44 + e06b=57 + e06c=58 + ;The following 12 items map fkeys to Fn+fkeys + 3b=e020 + 3c=e02e + 3d=e030 + 3e=e064 + 3f=e065 + 40=e028 + 41=e067 + 42=e068 + 43=e0f1 + 44=e06a + 57=e0fb + 58=e0fc + + Macro Inversion + + ;This section maps ps2 codes (packet format) received quickly (macros) into fake ps2 codes (packet format) + ;Fn+F4 + + //8CZAAAAAABOAE+ + + + //8C5AAAAAABvgG4 + + ;F5 (without Fn) + + //8CZQEAAAABPw== + + + //8C5QEAAAABvw== + + ;Fn+Ctrl+F6 + + //8CJwAD//8CZg== + + + //8CpwAD//8C5g== + + ;Ctrl+F6 + + //8CJwAD//8CQA== + + + //8CpwAD//8CwA== + + ;Fn+F8 + + //8CaAAAAAACHQE4AQ8= + + + //8C6AAAAAABjwG4Ap0= + + ;Fn+F10 + + //8CagAAAAACWwEZ + + + //8C6gAAAAABmQLb + + + Macro Translation + + ;This section maps ps2 codes (packet format) + modifiers to one more ps2 codes (packet format) + + MaximumMacroTime + 25000000 + + U430 + Haswell-Ideapad + + SECCSD + + LH43STAR + SamsungKeys + SamsungKeys + + Breakless PS2 + + e020 + e02e + e030 + + + + + + + OSBundleLibraries + + com.apple.iokit.IOHIDSystem + 1.1 + com.apple.kpi.bsd + 8.0.0 + com.apple.kpi.iokit + 8.0.0 + com.apple.kpi.libkern + 8.0.0 + com.apple.kpi.mach + 8.0.0 + com.apple.kpi.unsupported + 8.0.0 + org.rehabman.voodoo.driver.PS2Controller + 2.8.15 + + OSBundleRequired + Console + Source Code + https://github.com/RehabMan/OS-X-Voodoo-PS2-Controller + + diff --git a/VoodooPS2Keyboard/VoodooPS2Keyboard-Prefix.pch b/VoodooPS2Keyboard/VoodooPS2Keyboard-Prefix.pch new file mode 100644 index 0000000..e42c6c3 --- /dev/null +++ b/VoodooPS2Keyboard/VoodooPS2Keyboard-Prefix.pch @@ -0,0 +1,4 @@ +// +// Prefix header for all source files of the 'VoodooPS2Keyboard' target in the 'VoodooPS2Keyboard' project +// + diff --git a/VoodooPS2Keyboard/VoodooPS2Keyboard-RemapFN-Info.plist b/VoodooPS2Keyboard/VoodooPS2Keyboard-RemapFN-Info.plist new file mode 100644 index 0000000..e80c7f3 --- /dev/null +++ b/VoodooPS2Keyboard/VoodooPS2Keyboard-RemapFN-Info.plist @@ -0,0 +1,161 @@ + + + + + Source Code + https://github.com/RehabMan/OS-X-Voodoo-PS2-Controller + CFBundleGetInfoString + ${MODULE_VERSION}, Copyright Apple Computer, Inc. 2000-2003, RehabMan 2012-2013 + CFBundleExecutable + VoodooPS2Keyboard + CFBundleIdentifier + org.rehabman.voodoo.driver.PS2Keyboard + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + Voodoo PS/2 Keyboard + CFBundlePackageType + KEXT + CFBundleShortVersionString + ${MODULE_VERSION} + CFBundleSignature + ???? + CFBundleVersion + ${MODULE_VERSION} + IOKitPersonalities + + ApplePS2Keyboard + + CFBundleIdentifier + org.rehabman.voodoo.driver.PS2Keyboard + IOClass + ApplePS2Keyboard + IOProviderClass + ApplePS2KeyboardDevice + Platform Profile + + Default + + Make Application key into Apple Fn key + + Make Application key into right windows + + Make right modifier keys into Hangul and Hanja + + Swap capslock and left control + + Swap command and option + + Use ISO layout keyboard + + alt_handler_id + 3 + SleepPressTime + 3000 + Breakless PS2 + + ;Items must be strings in the form of breaklessscan (in hex) + + Function Keys Standard + + ;Items must be strings in the form of scanfrom=scanto (in hex) + ;The following 12 items map Fn+fkeys to Fn+fkeys + e05f=e05f + e012=e012 + e017=e017 + e06e=e06e + e00a=e00a + e009=e009 + e020=e020 + e02e=e02e + e030=e030 + e010=e010 + e022=e022 + e019=e019 + ;The following 12 items map fkeys to fkeys + 3b=3b + 3c=3c + 3d=3d + 3e=3e + 3f=3f + 40=40 + 41=41 + 42=42 + 43=43 + 44=44 + 57=57 + 58=58 + + Function Keys Special + + ;Items must be strings in the form of scanfrom=scanto (in hex) + ;The following 12 items map Fn+fkeys to fkeys + e05f=3b + e012=3c + e017=3d + e06e=3e + e00a=3f + e009=40 + e020=41 + e02e=42 + e030=43 + e010=44 + e022=57 + e019=58 + ;The following 12 items map fkeys to Fn+fkeys + 3b=e05f + 3c=e012 + 3d=e017 + 3e=e06e + 3f=e00a + 40=e009 + 41=e020 + 42=e02e + 43=e030 + 44=e010 + 57=e022 + 58=e019 + + Custom PS2 Map + + ;Items must be strings in the form of scanfrom=scanto (in hex) + + Custom ADB Map + + ;Items must be strings in the form of scanfrom=adbto (in hex) + + ActionSwipeUp + 3e d, 7e d, 7e u, 3e u< + ActionSwipeDown + 3b d, 37 d, 7d d, 7d u, 37 u, 3b u + ActionSwipeLeft + 3e d, 7c d, 7c u, 3e u + ActionSwipeRight + 3e d, 7b d, 7b u, 3e u + LogScanCodes + 0 + + + + + OSBundleLibraries + + com.apple.iokit.IOHIDSystem + 1.1 + com.apple.kpi.bsd + 8.0.0 + com.apple.kpi.iokit + 8.0.0 + com.apple.kpi.libkern + 8.0.0 + com.apple.kpi.mach + 8.0.0 + com.apple.kpi.unsupported + 8.0.0 + org.rehabman.voodoo.driver.PS2Controller + 2.8.15 + + OSBundleRequired + Console + + diff --git a/VoodooPS2Keyboard/VoodooPS2Keyboard.cpp b/VoodooPS2Keyboard/VoodooPS2Keyboard.cpp new file mode 100644 index 0000000..f43d811 --- /dev/null +++ b/VoodooPS2Keyboard/VoodooPS2Keyboard.cpp @@ -0,0 +1,2369 @@ +/* + * Copyright (c) 1998-2000 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * The contents of this file constitute Original Code as defined in and + * are subject to the Apple Public Source License Version 1.1 (the + * "License"). You may not use this file except in compliance with the + * License. Please obtain a copy of the License at + * http://www.apple.com/publicsource and read it before using this file. + * + * This Original Code and all software distributed under the License are + * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the + * License for the specific language governing rights and limitations + * under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +// enable for keyboard debugging +#ifdef DEBUG_MSG +//#define DEBUG_VERBOSE +#define DEBUG_LITE +#endif + +#include +#include +#include +#include +#include +#include "ApplePS2ToADBMap.h" +#include "VoodooPS2Controller.h" +#include "VoodooPS2Keyboard.h" +#include "ApplePS2ToADBMap.h" +#include "AppleACPIPS2Nub.h" +#include +#include + +//REVIEW: avoids problem with Xcode 5.1.0 where -dead_strip eliminates these required symbols +#include +void* _org_rehabman_dontstrip_[] = +{ + (void*)&OSKextGetCurrentIdentifier, + (void*)&OSKextGetCurrentLoadTag, + (void*)&OSKextGetCurrentVersionString, +}; + +// Constants for Info.plist settings + +#define kSleepPressTime "SleepPressTime" +#define kHIDFKeyMode "HIDFKeyMode" +#define kHIDF12EjectDelay "HIDF12EjectDelay" +#define kFunctionKeysStandard "Function Keys Standard" +#define kFunctionKeysSpecial "Function Keys Special" +#define kSwapCapsLockLeftControl "Swap capslock and left control" +#define kSwapCommandOption "Swap command and option" +#define kMakeApplicationKeyRightWindows "Make Application key into right windows" +#define kMakeApplicationKeyAppleFN "Make Application key into Apple Fn key" +#define kMakeRightModsHangulHanja "Make right modifier keys into Hangul and Hanja" +#define kUseISOLayoutKeyboard "Use ISO layout keyboard" +#define kLogScanCodes "LogScanCodes" +#define kActionSwipeUp "ActionSwipeUp" +#define kActionSwipeDown "ActionSwipeDown" +#define kActionSwipeLeft "ActionSwipeLeft" +#define kActionSwipeRight "ActionSwipeRight" +#define kBrightnessHack "BrightnessHack" +#define kMacroInversion "Macro Inversion" +#define kMacroTranslation "Macro Translation" +#define kMaxMacroTime "MaximumMacroTime" + +// Definitions for Macro Inversion data format +//REVIEW: This should really be defined as some sort of structure +#define kIgnoreBytes 2 // first two bytes of macro data are ignored (always 0xffff) +#define kOutputBytes 2 // two bytes of Macro Inversion are used to specify output +#define kModifierBytes 4 // 4 bytes specify modifier key match criteria +#define kOutputBytesOffset (kIgnoreBytes+0) +#define kModifierBytesOffset (kIgnoreBytes+kOutputBytes+0) +#define kPrefixBytes (kIgnoreBytes+kOutputBytes+kModifierBytes) +#define kSequenceBytesOffset (kPrefixBytes+0) +#define kMinMacroInversion (kPrefixBytes+2) + +// Constants for other services to communicate with + +#define kIOHIDSystem "IOHIDSystem" + +// ============================================================================= +// ApplePS2Keyboard Class Implementation +// + +// get some keyboard id information from IOHIDFamily/IOHIDKeyboard.h and Gestalt.h +//#define APPLEPS2KEYBOARD_DEVICE_TYPE 205 // Generic ISO keyboard +#define APPLEPS2KEYBOARD_DEVICE_TYPE 3 // Unknown ANSI keyboard + +OSDefineMetaClassAndStructors(ApplePS2Keyboard, IOHIKeyboard); + +UInt32 ApplePS2Keyboard::deviceType() +{ + OSNumber *xml_handlerID; + UInt32 ret_id; + + if ( (xml_handlerID = OSDynamicCast( OSNumber, getProperty("alt_handler_id"))) ) + ret_id = xml_handlerID->unsigned32BitValue(); + else + ret_id = APPLEPS2KEYBOARD_DEVICE_TYPE; + + return ret_id; +} + +UInt32 ApplePS2Keyboard::interfaceID() { return NX_EVS_DEVICE_INTERFACE_ADB; } + +UInt32 ApplePS2Keyboard::maxKeyCodes() { return NX_NUMKEYCODES; } + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +static const char* parseHex(const char *psz, char term1, char term2, unsigned& out) +{ + int n = 0; + for (; 0 != *psz && term1 != *psz && term2 != *psz; ++psz) + { + n <<= 4; + if (*psz >= '0' && *psz <= '9') + n += *psz - '0'; + else if (*psz >= 'a' && *psz <= 'f') + n += *psz - 'a' + 10; + else if (*psz >= 'A' && *psz <= 'F') + n += *psz - 'A' + 10; + else + return NULL; + } + out = n; + return psz; +} + +static bool parseRemap(const char *psz, UInt16 &scanFrom, UInt16& scanTo) +{ + // psz is of the form: "scanfrom=scanto", examples: + // non-extended: "1d=3a" + // extended: "e077=e017" + // of course, extended can be mapped to non-extended or non-extended to extended + + unsigned n; + psz = parseHex(psz, '=', 0, n); + if (NULL == psz || *psz != '=' || n > 0xFFFF) + return false; + scanFrom = n; + psz = parseHex(psz+1, '\n', ';', n); + if (NULL == psz || n > 0xFFFF) + return false; + scanTo = n; + return true; +} + +static bool parseAction(const char* psz, UInt16 dest[], int size) +{ + int i = 0; + while (*psz && i < size) + { + unsigned n; + psz = parseHex(psz, ' ', 0, n); + if (!psz || *psz != ' ') + goto error; + ++psz; + if (*psz != 'd' && *psz != 'u') + goto error; + dest[i++] = n | (*psz == 'u' ? 0x1000 : 0); + ++psz; + if (!*psz) + break; + if (*psz != ',') + goto error; + while (*++psz == ' '); + } + if (i >= size) + goto error; + + dest[i] = 0; + return true; + +error: + dest[0] = 0; + return false; +} + +#ifdef DEBUG +static void logKeySequence(const char* header, UInt16* pAction) +{ + DEBUG_LOG("ApplePS2Keyboard: %s { ", header); + for (; *pAction; ++pAction) + { + DEBUG_LOG("%04x, ", *pAction); + } + DEBUG_LOG("}\n"); +} +#endif + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +bool ApplePS2Keyboard::init(OSDictionary * dict) +{ + // + // Initialize this object's minimal state. This is invoked right after this + // object is instantiated. + // + + if (!super::init(dict)) + return false; + + // find config specific to Platform Profile + OSDictionary* list = OSDynamicCast(OSDictionary, dict->getObject(kPlatformProfile)); + OSDictionary* config = ApplePS2Controller::makeConfigurationNode(list); + if (config) + { + // if DisableDevice is Yes, then do not load at all... + OSBoolean* disable = OSDynamicCast(OSBoolean, config->getObject(kDisableDevice)); + if (disable && disable->isTrue()) + { + config->release(); + return false; + } +#ifdef DEBUG + // save configuration for later/diagnostics... + setProperty(kMergedConfiguration, config); +#endif + } + + // initialize state + _device = 0; + _extendCount = 0; + _interruptHandlerInstalled = false; + _ledState = 0; + _lastdata = 0; + + _swapcommandoption = false; + _sleepEjectTimer = 0; + _cmdGate = 0; + + _fkeymode = 0; + _fkeymodesupported = false; + _keysStandard = 0; + _keysSpecial = 0; + _f12ejectdelay = 250; // default is 250 ms + + // initialize ACPI support for keyboard backlight/screen brightness + _provider = 0; + _brightnessLevels = 0; + _backlightLevels = 0; + + _logscancodes = 0; + _brightnessHack = false; + + // initalize macro translation + _macroInversion = 0; + _macroTranslation = 0; + _macroBuffer = 0; + _macroCurrent = 0; + _macroMax = 0; + _macroMaxTime = 25000000ULL; + _macroTimer = 0; + + // start out with all keys up + bzero(_keyBitVector, sizeof(_keyBitVector)); + + // make separate copy of ADB translation table. + bcopy(PS2ToADBMapStock, _PS2ToADBMapMapped, sizeof(_PS2ToADBMapMapped)); + + // Setup the PS2 -> PS2 scan code mapper + for (int i = 0; i < countof(_PS2ToPS2Map); i++) + { + // by default, each map entry is just itself (no mapping) + // first half of map is normal scan codes, second half is extended scan codes (e0) + _PS2ToPS2Map[i] = i; + } + bcopy(_PS2flagsStock, _PS2flags, sizeof(_PS2flags)); + + // Setup default swipe actions + parseAction("3b d, 37 d, 7e d, 7e u, 37 u, 3b u", _actionSwipeUp, countof(_actionSwipeUp)); + parseAction("3b d, 37 d, 7d d, 7d u, 37 u, 3b u", _actionSwipeDown, countof(_actionSwipeDown)); + parseAction("3b d, 37 d, 7b d, 7b u, 37 u, 3b u", _actionSwipeLeft, countof(_actionSwipeLeft)); + parseAction("3b d, 37 d, 7c d, 7c u, 37 u, 3b u", _actionSwipeRight, countof(_actionSwipeRight)); + + parseAction("3b d, 3a d, 7e d, 7e u, 3a u, 3b u", _actionSwipe4Up, countof(_actionSwipe4Up)); + parseAction("3b d, 3a d, 7d d, 7d u, 3a u, 3b u", _actionSwipe4Down, countof(_actionSwipe4Down)); + parseAction("3b d, 3a d, 7b d, 7b u, 3a u, 3b u", _actionSwipe4Left, countof(_actionSwipe4Left)); + parseAction("3b d, 3a d, 7c d, 7c u, 3a u, 3b u", _actionSwipe4Right, countof(_actionSwipe4Right)); + + // + // Load settings specfic to the Platform Profile... + // + + if (config) + { + // now load PS2 -> PS2 configuration data + loadCustomPS2Map(OSDynamicCast(OSArray, config->getObject("Custom PS2 Map"))); + loadBreaklessPS2(config, "Breakless PS2"); + + // now load PS2 -> ADB configuration data + loadCustomADBMap(config, "Custom ADB Map"); + + // determine if _fkeymode property should be handled in setParamProperties + _keysStandard = OSDynamicCast(OSArray, config->getObject(kFunctionKeysStandard)); + _keysSpecial = OSDynamicCast(OSArray, config->getObject(kFunctionKeysSpecial)); + _fkeymodesupported = _keysStandard && _keysSpecial; + if (_fkeymodesupported) + { + setProperty(kHIDFKeyMode, (uint64_t)0, 64); + _keysStandard->retain(); + _keysSpecial->retain(); + loadCustomPS2Map(_keysSpecial); + } + else + { + OSSafeReleaseNULL(_keysStandard); + OSSafeReleaseNULL(_keysSpecial); + } + + // load custom macro data + _macroTranslation = loadMacroData(config, kMacroTranslation); + _macroInversion = loadMacroData(config, kMacroInversion); + if (_macroInversion) + { + int max = 0; + for (OSData** p = _macroInversion; *p; p++) + { + int length = (*p)->getLength()-kPrefixBytes; + if (length > max) + max = length; + } + _macroBuffer = new UInt8[max*kPacketLength]; + _macroMax = max; + } + } + + // now copy to our PS2ToADBMap -- working copy... + bcopy(_PS2ToADBMapMapped, _PS2ToADBMap, sizeof(_PS2ToADBMap)); + + // populate rest of values via setParamProperties + setParamPropertiesGated(config); + OSSafeReleaseNULL(config); + +#ifdef DEBUG + logKeySequence("Swipe Up:", _actionSwipeUp); + logKeySequence("Swipe Down:", _actionSwipeDown); + logKeySequence("Swipe Left:", _actionSwipeLeft); + logKeySequence("Swipe Right:", _actionSwipeRight); + logKeySequence("Swipe 4 Up:", _actionSwipe4Up); + logKeySequence("Swipe 4 Down:", _actionSwipe4Down); + logKeySequence("Swipe 4 Left:", _actionSwipe4Left); + logKeySequence("Swipe 4 Right:", _actionSwipe4Right); +#endif + + return true; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +void ApplePS2Keyboard::free() +{ + OSSafeReleaseNULL(_keysStandard); + OSSafeReleaseNULL(_keysSpecial); + + if (_macroInversion) + { + delete[] _macroInversion; + _macroInversion = 0; + } + if (_macroTranslation) + { + delete[] _macroTranslation; + _macroTranslation = 0; + } + if (_macroBuffer) + { + delete[] _macroBuffer; + _macroBuffer = 0; + } + + super::free(); +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +ApplePS2Keyboard* ApplePS2Keyboard::probe(IOService * provider, SInt32 * score) +{ + DEBUG_LOG("ApplePS2Keyboard::probe entered...\n"); + + // + // The driver has been instructed to verify the presence of the actual + // hardware we represent. We are guaranteed by the controller that the + // keyboard clock is enabled and the keyboard itself is disabled (thus + // it won't send any asynchronous scan codes that may mess up the + // responses expected by the commands we send it). This is invoked + // after the init. + // + + if (!super::probe(provider, score)) + return 0; + + // Note: always return success for keyboard, so no need to do this! + // But we do it in the DEBUG build just for information's sake. +#ifdef DEBUG + ApplePS2KeyboardDevice * device = (ApplePS2KeyboardDevice *)provider; + + // + // Check to see if the keyboard responds to a basic diagnostic echo. + // + + // (diagnostic echo command) + TPS2Request<2> request; + request.commands[0].command = kPS2C_WriteDataPort; + request.commands[0].inOrOut = kDP_TestKeyboardEcho; + request.commands[1].command = kPS2C_ReadDataPort; + request.commands[1].inOrOut = 0x00; + request.commandsCount = 2; + assert(request.commandsCount <= countof(request.commands)); + device->submitRequestAndBlock(&request); + UInt8 result = request.commands[1].inOrOut; + if (2 != request.commandsCount || (0x00 != result && kDP_TestKeyboardEcho != result && kSC_Acknowledge != result)) + { + IOLog("%s: TestKeyboardEcho $EE failed: %d (%02x)\n", getName(), request.commandsCount, result); + IOLog("%s: ApplePS2Keyboard::probe would normally return failure\n", getName()); + } +#endif + + DEBUG_LOG("ApplePS2Keyboard::probe leaving.\n"); + + // Note: forced success regardless of "test keyboard echo" + return this; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +bool ApplePS2Keyboard::start(IOService * provider) +{ + DEBUG_LOG("ApplePS2Keyboard::start entered...\n"); + + // + // The driver has been instructed to start. This is called after a + // successful attach. + // + + if (!super::start(provider)) + return false; + + // + // Maintain a pointer to and retain the provider object. + // + + _device = (ApplePS2KeyboardDevice *)provider; + _device->retain(); + + // + // Setup workloop with command gate for thread syncronization... + // + IOWorkLoop* pWorkLoop = getWorkLoop(); + _cmdGate = IOCommandGate::commandGate(this); + if (!pWorkLoop || !_cmdGate) + { + _device->release(); + _device = 0; + return false; + } + _sleepEjectTimer = IOTimerEventSource::timerEventSource(this, OSMemberFunctionCast(IOTimerEventSource::Action, this, &ApplePS2Keyboard::onSleepEjectTimer)); + if (!_sleepEjectTimer) + { + _cmdGate->release(); + _cmdGate = 0; + _device->release(); + _device = 0; + return false; + } + pWorkLoop->addEventSource(_sleepEjectTimer); + pWorkLoop->addEventSource(_cmdGate); + + // _macroTimer is used in for macro inversion + _macroTimer = IOTimerEventSource::timerEventSource(this, OSMemberFunctionCast(IOTimerEventSource::Action, this, &ApplePS2Keyboard::onMacroTimer)); + if (_macroTimer) + pWorkLoop->addEventSource(_macroTimer); + + // get IOACPIPlatformDevice for Device (PS2K) + //REVIEW: should really look at the parent chain for IOACPIPlatformDevice instead. + _provider = (IOACPIPlatformDevice*)IORegistryEntry::fromPath("IOService:/AppleACPIPlatformExpert/PS2K"); + + // + // get brightness levels for ACPI based brightness keys + // + + OSObject* result = 0; + if (_provider) do + { + // check for brightness methods + if (kIOReturnSuccess != _provider->validateObject("KBCL") || kIOReturnSuccess != _provider->validateObject("KBCM") || kIOReturnSuccess != _provider->validateObject("KBQC")) + { + break; + } + // methods are there, so now try to collect brightness levels + if (kIOReturnSuccess != _provider->evaluateObject("KBCL", &result)) + { + DEBUG_LOG("ps2br: KBCL returned error\n"); + break; + } + OSArray* array = OSDynamicCast(OSArray, result); + if (!array) + { + DEBUG_LOG("ps2br: KBCL returned non-array package\n"); + break; + } + int count = array->getCount(); + if (count < 4) + { + DEBUG_LOG("ps2br: KBCL returned invalid package\n"); + break; + } + _brightnessCount = count; + _brightnessLevels = new int[_brightnessCount]; + if (!_brightnessLevels) + { + DEBUG_LOG("ps2br: _brightnessLevels new int[] failed\n"); + break; + } + for (int i = 0; i < _brightnessCount; i++) + { + OSNumber* num = OSDynamicCast(OSNumber, array->getObject(i)); + int brightness = num ? num->unsigned32BitValue() : 0; + _brightnessLevels[i] = brightness; + } +#ifdef DEBUG_VERBOSE + DEBUG_LOG("ps2br: Brightness levels: { "); + for (int i = 0; i < _brightnessCount; i++) + DEBUG_LOG("%d, ", _brightnessLevels[i]); + DEBUG_LOG("}\n"); +#endif + break; + } while (false); + + OSSafeReleaseNULL(result); + + // + // get keyboard backlight levels for ACPI based backlight keys + // + + if (_provider) do + { + // check for brightness methods + if (kIOReturnSuccess != _provider->validateObject("KKCL") || kIOReturnSuccess != _provider->validateObject("KKCM") || kIOReturnSuccess != _provider->validateObject("KKQC")) + { + DEBUG_LOG("ps2bl: KKCL, KKCM, KKQC methods not found in DSDT\n"); + break; + } + + // methods are there, so now try to collect brightness levels + if (kIOReturnSuccess != _provider->evaluateObject("KKCL", &result)) + { + DEBUG_LOG("ps2bl: KKCL returned error\n"); + break; + } + OSArray* array = OSDynamicCast(OSArray, result); + if (!array) + { + DEBUG_LOG("ps2bl: KKCL returned non-array package\n"); + break; + } + int count = array->getCount(); + if (count < 2) + { + DEBUG_LOG("ps2bl: KKCL returned invalid package\n"); + break; + } + _backlightCount = count; + _backlightLevels = new int[_backlightCount]; + if (!_backlightLevels) + { + DEBUG_LOG("ps2bl: _backlightLevels new int[] failed\n"); + break; + } + for (int i = 0; i < _backlightCount; i++) + { + OSNumber* num = OSDynamicCast(OSNumber, array->getObject(i)); + int brightness = num ? num->unsigned32BitValue() : 0; + _backlightLevels[i] = brightness; + } +#ifdef DEBUG_VERBOSE + DEBUG_LOG("ps2bl: Keyboard backlight levels: { "); + for (int i = 0; i < _backlightCount; i++) + DEBUG_LOG("%d, ", _backlightLevels[i]); + DEBUG_LOG("}\n"); +#endif + break; + } while (false); + + OSSafeReleaseNULL(result); + + // + // Lock the controller during initialization + // + + _device->lock(); + + // + // Reset and enable the keyboard. + // + + initKeyboard(); + + // + // Install our driver's interrupt handler, for asynchronous data delivery. + // + + _device->installInterruptAction(this, + OSMemberFunctionCast(PS2InterruptAction, this, &ApplePS2Keyboard::interruptOccurred), + OSMemberFunctionCast(PS2PacketAction,this,&ApplePS2Keyboard::packetReady)); + _interruptHandlerInstalled = true; + + // now safe to allow other threads + _device->unlock(); + + // + // Install our power control handler. + // + + _device->installPowerControlAction( this, + OSMemberFunctionCast(PS2PowerControlAction,this, &ApplePS2Keyboard::setDevicePowerState )); + _powerControlHandlerInstalled = true; + + // + // Install our message handler. + // + _device->installMessageAction( this, + OSMemberFunctionCast(PS2MessageAction, this, &ApplePS2Keyboard::receiveMessage)); + _messageHandlerInstalled = true; + + // + // Tell ACPIPS2Nub that we are interested in ACPI notifications + // + setProperty(kDeliverNotifications, true); + + DEBUG_LOG("ApplePS2Keyboard::start leaving.\n"); + + return true; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +void ApplePS2Keyboard::loadCustomPS2Map(OSArray* pArray) +{ + if (NULL != pArray) + { + int count = pArray->getCount(); + for (int i = 0; i < count; i++) + { + OSString* pString = OSDynamicCast(OSString, pArray->getObject(i)); + if (NULL == pString) + continue; + const char* psz = pString->getCStringNoCopy(); + // check for comment + if (';' == *psz) + continue; + // otherwise, try to parse it + UInt16 scanIn, scanOut; + if (!parseRemap(psz, scanIn, scanOut)) + { + IOLog("VoodooPS2Keyboard: invalid custom PS2 map entry: \"%s\"\n", psz); + continue; + } + // must be normal scan code or extended, nothing else + UInt8 exIn = scanIn >> 8; + UInt8 exOut = scanOut >> 8; + if ((exIn != 0 && exIn != 0xe0) || (exOut != 0 && exOut != 0xe0)) + { + IOLog("VoodooPS2Keyboard: scan code invalid for PS2 map entry: \"%s\"\n", psz); + continue; + } + // modify PS2 to PS2 map per remap entry + int index = (scanIn & 0xff) + (exIn == 0xe0 ? KBV_NUM_SCANCODES : 0); + assert(index < countof(_PS2ToPS2Map)); + _PS2ToPS2Map[index] = (scanOut & 0xff) + (exOut == 0xe0 ? KBV_NUM_SCANCODES : 0); + } + } +} + +void ApplePS2Keyboard::loadBreaklessPS2(OSDictionary* dict, const char* name) +{ + OSArray* pArray = OSDynamicCast(OSArray, dict->getObject(name)); + if (NULL != pArray) + { + int count = pArray->getCount(); + for (int i = 0; i < count; i++) + { + OSString* pString = OSDynamicCast(OSString, pArray->getObject(i)); + if (NULL == pString) + continue; + const char* psz = pString->getCStringNoCopy(); + // check for comment + if (';' == *psz) + continue; + // otherwise, try to parse it + unsigned scanIn; + if (!parseHex(psz, '\n', ';', scanIn)) + { + IOLog("VoodooPS2Keyboard: invalid breakless PS2 entry: \"%s\"\n", psz); + continue; + } + // must be normal scan code or extended, nothing else + UInt8 exIn = scanIn >> 8; + if ((exIn != 0 && exIn != 0xe0)) + { + IOLog("VoodooPS2Keyboard: scan code invalid for breakless PS2 entry: \"%s\"\n", psz); + continue; + } + // modify PS2 to PS2 map per remap entry + int index = (scanIn & 0xff) + (exIn == 0xe0 ? KBV_NUM_SCANCODES : 0); + assert(index < countof(_PS2flags)); + _PS2flags[index] |= kBreaklessKey; + } + } +} + +void ApplePS2Keyboard::loadCustomADBMap(OSDictionary* dict, const char* name) +{ + OSArray* pArray = OSDynamicCast(OSArray, dict->getObject(name)); + if (NULL != pArray) + { + int count = pArray->getCount(); + for (int i = 0; i < count; i++) + { + OSString* pString = OSDynamicCast(OSString, pArray->getObject(i)); + if (NULL == pString) + continue; + const char* psz = pString->getCStringNoCopy(); + // check for comment + if (';' == *psz) + continue; + // otherwise, try to parse it + UInt16 scanIn, adbOut; + if (!parseRemap(psz, scanIn, adbOut)) + { + IOLog("VoodooPS2Keyboard: invalid custom ADB map entry: \"%s\"\n", psz); + continue; + } + // must be normal scan code or extended, nothing else, adbOut is only a byte + UInt8 exIn = scanIn >> 8; + if ((exIn != 0 && exIn != 0xe0) || adbOut > 0xFF) + { + IOLog("VoodooPS2Keyboard: scan code invalid for ADB map entry: \"%s\"\n", psz); + continue; + } + // modify PS2 to ADB map per remap entry + int index = (scanIn & 0xff) + (exIn == 0xe0 ? ADB_CONVERTER_EX_START : 0); + assert(index < countof(_PS2ToADBMapMapped)); + _PS2ToADBMapMapped[index] = adbOut; + } + } +} + +OSData** ApplePS2Keyboard::loadMacroData(OSDictionary* dict, const char* name) +{ + OSData** result = 0; + OSArray* pArray = OSDynamicCast(OSArray, dict->getObject(name)); + if (NULL != pArray) + { + // count valid entries + int total = 0; + int count = pArray->getCount(); + for (int i = 0; i < count; i++) + { + if (OSData* pData = OSDynamicCast(OSData, pArray->getObject(i))) + { + int length = pData->getLength(); + if (length >= kMinMacroInversion && !(length & 0x01)) + { + const UInt8* p = static_cast(pData->getBytesNoCopy()); + if (p[0] == 0xFF && p[1] == 0xFF) + total++; + } + } + } + if (total) + { + // store valid entries + result = new OSData*[total+1]; + if (result) + { + bzero(result, sizeof(char*)*(total+1)); + int index = 0; + for (int i = 0; i < count; i++) + { + if (OSData* pData = OSDynamicCast(OSData, pArray->getObject(i))) + { + int length = pData->getLength(); + if (length >= kMinMacroInversion && !(length & 0x01)) + { + const UInt8* p = static_cast(pData->getBytesNoCopy()); + if (p[0] == 0xFF && p[1] == 0xFF) + result[index++] = pData; + } + } + } + } + } + } + return result; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +void ApplePS2Keyboard::setParamPropertiesGated(OSDictionary * dict) +{ + if (NULL == dict) + return; + + //REVIEW: this code needs cleanup (should be table driven like mouse/trackpad) + + // get time before sleep button takes effect + if (OSNumber* num = OSDynamicCast(OSNumber, dict->getObject(kSleepPressTime))) + { + _maxsleeppresstime = num->unsigned32BitValue(); + setProperty(kSleepPressTime, _maxsleeppresstime, 32); + } + // get time before eject button takes effect (no modifiers) + if (OSNumber* num = OSDynamicCast(OSNumber, dict->getObject(kHIDF12EjectDelay))) + { + _f12ejectdelay = num->unsigned32BitValue(); + setProperty(kHIDF12EjectDelay, _f12ejectdelay, 32); + } + // get time between keys part of a macro "inversion" + if (OSNumber* num = OSDynamicCast(OSNumber, dict->getObject(kMaxMacroTime))) + { + _macroMaxTime = num->unsigned64BitValue(); + setProperty(kMaxMacroTime, _macroMaxTime, 64); + } + + if (_fkeymodesupported) + { + // get function key mode + UInt32 oldfkeymode = _fkeymode; + if (OSNumber* num = OSDynamicCast(OSNumber, dict->getObject(kHIDFKeyMode))) + { + _fkeymode = num->unsigned32BitValue(); + setProperty(kHIDFKeyMode, _fkeymode, 32); + } + if (oldfkeymode != _fkeymode) + { + OSArray* keys = _fkeymode ? _keysStandard : _keysSpecial; + assert(keys); + loadCustomPS2Map(keys); + } + } + + // + // Configure user preferences from Info.plist + // + OSBoolean* xml = OSDynamicCast(OSBoolean, dict->getObject(kSwapCapsLockLeftControl)); + if (xml) { + if (xml->isTrue()) { + _PS2ToADBMap[0x3a] = _PS2ToADBMapMapped[0x1d]; + _PS2ToADBMap[0x1d] = _PS2ToADBMapMapped[0x3a]; + } + else { + _PS2ToADBMap[0x3a] = _PS2ToADBMapMapped[0x3a]; + _PS2ToADBMap[0x1d] = _PS2ToADBMapMapped[0x1d]; + } + setProperty(kSwapCapsLockLeftControl, xml->isTrue() ? kOSBooleanTrue : kOSBooleanFalse); + } + + xml = OSDynamicCast(OSBoolean, dict->getObject(kSwapCommandOption)); + if (xml) { + if (xml->isTrue()) { + _swapcommandoption = true; + _PS2ToADBMap[0x38] = _PS2ToADBMapMapped[0x15b]; + _PS2ToADBMap[0x15b] = _PS2ToADBMapMapped[0x38]; + _PS2ToADBMap[0x138] = _PS2ToADBMapMapped[0x15c]; + _PS2ToADBMap[0x15c] = _PS2ToADBMapMapped[0x138]; + } + else { + _swapcommandoption = false; + _PS2ToADBMap[0x38] = _PS2ToADBMapMapped[0x38]; + _PS2ToADBMap[0x15b] = _PS2ToADBMapMapped[0x15b]; + _PS2ToADBMap[0x138] = _PS2ToADBMapMapped[0x138]; + _PS2ToADBMap[0x15c] = _PS2ToADBMapMapped[0x15c]; + } + setProperty(kSwapCommandOption, xml->isTrue() ? kOSBooleanTrue : kOSBooleanFalse); + } + + // special hack for HP Envy brightness + xml = OSDynamicCast(OSBoolean, dict->getObject(kBrightnessHack)); + if (xml && xml->isTrue()) + { + //REVIEW: should really read the key assignments via Info.plist instead of hardcoding to F2/F3 + _brightnessHack = true; + } + + // these two options are mutually exclusive + // kMakeApplicationKeyAppleFN is ignored if kMakeApplicationKeyRightWindows is set + bool temp = false; + xml = OSDynamicCast(OSBoolean, dict->getObject(kMakeApplicationKeyRightWindows)); + if (xml) { + if (xml->isTrue()) { + _PS2ToADBMap[0x15d] = _swapcommandoption ? 0x3d : 0x36; // ADB = right-option/right-command + temp = true; + } + else { + _PS2ToADBMap[0x15d] = _PS2ToADBMapMapped[0x15d]; + } + setProperty(kMakeApplicationKeyRightWindows, xml->isTrue() ? kOSBooleanTrue : kOSBooleanFalse); + } + // not implemented yet (Note: maybe not true any more) + // Apple Fn key works well, but no combined key action was made. + xml = OSDynamicCast(OSBoolean, dict->getObject(kMakeApplicationKeyAppleFN)); + if (xml) { + if (!temp) { + if (xml->isTrue()) { + _PS2ToADBMap[0x15d] = 0x3f; // ADB = AppleFN + } + else { + _PS2ToADBMap[0x15d] = _PS2ToADBMapMapped[0x15d]; + } + } + setProperty(kMakeApplicationKeyAppleFN, xml->isTrue() ? kOSBooleanTrue : kOSBooleanFalse); + } + + xml = OSDynamicCast(OSBoolean, dict->getObject(kMakeRightModsHangulHanja)); + if (xml) { + if (xml->isTrue()) { + _PS2ToADBMap[0x138] = _PS2ToADBMapMapped[0xf2]; // Right alt becomes Hangul + _PS2ToADBMap[0x11d] = _PS2ToADBMapMapped[0xf1]; // Right control becomes Hanja + } + else { + if (_swapcommandoption) + _PS2ToADBMap[0x138] = _PS2ToADBMapMapped[0x15c]; + else + _PS2ToADBMap[0x138] = _PS2ToADBMapMapped[0x138]; + _PS2ToADBMap[0x11d] = _PS2ToADBMapMapped[0x11d]; + } + setProperty(kMakeRightModsHangulHanja, xml->isTrue() ? kOSBooleanTrue : kOSBooleanFalse); + } + + // ISO specific mapping to match ADB keyboards + // This should really be done in the keymaps. + xml = OSDynamicCast(OSBoolean, dict->getObject(kUseISOLayoutKeyboard)); + if (xml) { + if (xml->isTrue()) { + _PS2ToADBMap[0x29] = _PS2ToADBMapMapped[0x56]; //Europe2 '¤º' + _PS2ToADBMap[0x56] = _PS2ToADBMapMapped[0x29]; //Grave '~' + } + else { + _PS2ToADBMap[0x29] = _PS2ToADBMapMapped[0x29]; + _PS2ToADBMap[0x56] = _PS2ToADBMapMapped[0x56]; + } + setProperty(kUseISOLayoutKeyboard, xml->isTrue() ? kOSBooleanTrue : kOSBooleanFalse); + } + + if (OSNumber* num = OSDynamicCast(OSNumber, dict->getObject(kLogScanCodes))) { + _logscancodes = num->unsigned32BitValue(); + setProperty(kLogScanCodes, num); + } + + // now load swipe Action configuration data + OSString* str = OSDynamicCast(OSString, dict->getObject(kActionSwipeUp)); + if (str) + { + parseAction(str->getCStringNoCopy(), _actionSwipeUp, countof(_actionSwipeUp)); + setProperty(kActionSwipeUp, str); + } + + str = OSDynamicCast(OSString, dict->getObject(kActionSwipeDown)); + if (str) + { + parseAction(str->getCStringNoCopy(), _actionSwipeDown, countof(_actionSwipeDown)); + setProperty(kActionSwipeDown, str); + } + + str = OSDynamicCast(OSString, dict->getObject(kActionSwipeLeft)); + if (str) + { + parseAction(str->getCStringNoCopy(), _actionSwipeLeft, countof(_actionSwipeLeft)); + setProperty(kActionSwipeLeft, str); + } + + str = OSDynamicCast(OSString, dict->getObject(kActionSwipeRight)); + if (str) + { + parseAction(str->getCStringNoCopy(), _actionSwipeRight, countof(_actionSwipeRight)); + setProperty(kActionSwipeRight, str); + } +} + +IOReturn ApplePS2Keyboard::setParamProperties(OSDictionary *dict) +{ + ////IOReturn result = super::setParamProperties(dict); + if (_cmdGate) + { + // syncronize through workloop... + ////_cmdGate->runAction(OSMemberFunctionCast(IOCommandGate::Action, this, &ApplePS2Keyboard::setParamPropertiesGated), dict); + setParamPropertiesGated(dict); + } + + return super::setParamProperties(dict); + ////return result; +} + +IOReturn ApplePS2Keyboard::setProperties(OSObject *props) +{ + OSDictionary *dict = OSDynamicCast(OSDictionary, props); + if (dict && _cmdGate) + { + // syncronize through workloop... + _cmdGate->runAction(OSMemberFunctionCast(IOCommandGate::Action, this, &ApplePS2Keyboard::setParamPropertiesGated), dict); + } + + return super::setProperties(props); +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +void ApplePS2Keyboard::stop(IOService * provider) +{ + // + // The driver has been instructed to stop. Note that we must break all + // connections to other service objects now (ie. no registered actions, + // no pointers and retains to objects, etc), if any. + // + + assert(_device == provider); + + // + // Disable the keyboard itself, so that it may stop reporting key events. + // + + setKeyboardEnable(false); + + // free up the command gate + IOWorkLoop* pWorkLoop = getWorkLoop(); + if (pWorkLoop) + { + if (_cmdGate) + { + pWorkLoop->removeEventSource(_cmdGate); + _cmdGate->release(); + _cmdGate = 0; + } + if (_sleepEjectTimer) + { + pWorkLoop->removeEventSource(_sleepEjectTimer); + _sleepEjectTimer->release(); + _sleepEjectTimer = 0; + } + if (_macroTimer) + { + pWorkLoop->removeEventSource(_macroTimer); + _macroTimer->release(); + _macroTimer = 0; + } + } + + // + // Uninstall the interrupt handler. + // + + if ( _interruptHandlerInstalled ) _device->uninstallInterruptAction(); + _interruptHandlerInstalled = false; + + // + // Uninstall the power control handler. + // + + if ( _powerControlHandlerInstalled ) _device->uninstallPowerControlAction(); + _powerControlHandlerInstalled = false; + + // + // Uninstall the message handler. + // + + if ( _messageHandlerInstalled ) _device->uninstallMessageAction(); + _messageHandlerInstalled = false; + + // + // Release the pointer to the provider object. + // + + OSSafeReleaseNULL(_device); + + // + // Release ACPI provider for PS2K ACPI device + // + OSSafeReleaseNULL(_provider); + + // + // Release data related to screen brightness + // + if (_brightnessLevels) + { + delete[] _brightnessLevels; + _brightnessLevels = 0; + } + + // + // Release data related to screen brightness + // + if (_backlightLevels) + { + delete[] _backlightLevels; + _backlightLevels = 0; + } + + super::stop(provider); +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +IOReturn ApplePS2Keyboard::message(UInt32 type, IOService* provider, void* argument) +{ +#ifdef DEBUG + if (argument) + IOLog("ApplePS2Keyboard::message: type=%x, provider=%p, argument=%p, argument=%04x, cmp=%x\n", type, provider, argument, *static_cast(argument), kIOACPIMessageDeviceNotification); + else + IOLog("ApplePS2Keyboard::message: type=%x, provider=%p", type, provider); +#endif + + if (type == kIOACPIMessageDeviceNotification && NULL != argument) + { + UInt32 arg = *static_cast(argument); + if ((arg & 0xFFFF0000) == 0) + { + UInt8 packet[kPacketLength]; + packet[0] = arg >> 8; + packet[1] = arg; + if (1 == packet[0] || 2 == packet[0]) + { + // mark packet with timestamp + clock_get_uptime((uint64_t*)(&packet[kPacketTimeOffset])); + if (!_macroInversion || !invertMacros(packet)) + { + // normal packet + dispatchKeyboardEventWithPacket(packet); + } + } + if (3 == packet[0] || 4 == packet[0]) + { + // code 3 and 4 indicate send both make and break + packet[0] -= 2; + clock_get_uptime((uint64_t*)(&packet[kPacketTimeOffset])); + if (!_macroInversion || !invertMacros(packet)) + { + // normal packet (make) + dispatchKeyboardEventWithPacket(packet); + } + clock_get_uptime((uint64_t*)(&packet[kPacketTimeOffset])); + packet[1] |= 0x80; // break code + if (!_macroInversion || !invertMacros(packet)) + { + // normal packet (break) + dispatchKeyboardEventWithPacket(packet); + } + } + } + } + return kIOReturnSuccess; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +PS2InterruptResult ApplePS2Keyboard::interruptOccurred(UInt8 data) // PS2InterruptAction +{ + ////IOLog("ps2interrupt: scanCode = %02x\n", data); + ////uint64_t time; + ////clock_get_uptime(&time); + ////IOLog("ps2interrupt(%lld): scanCode = %02x\n", time, data); + + // + // This will be invoked automatically from our device when asynchronous + // keyboard data needs to be delivered. Process the keyboard data. Do + // NOT send any BLOCKING commands to our device in this context. + // + + UInt8* packet = _ringBuffer.head(); + + // special case for $AA $00, spontaneous reset (usually due to static electricity) + if (kSC_Reset == _lastdata && 0x00 == data) + { + IOLog("%s: Unexpected reset (%02x %02x) request from PS/2 controller.\n", getName(), _lastdata, data); + + // buffer a packet that will cause a reset in work loop + packet[0] = 0x00; + packet[1] = kSC_Reset; + // mark packet with timestamp + clock_get_uptime((uint64_t*)(&packet[kPacketTimeOffset])); + _ringBuffer.advanceHead(kPacketLength); + _extendCount = 0; + return kPS2IR_packetReady; + } + _lastdata = data; + + // other data error conditions + if (kSC_Acknowledge == data) + { + IOLog("%s: Unexpected acknowledge (%02x) from PS/2 controller.\n", getName(), data); + return kPS2IR_packetBuffering; + } + if (kSC_Resend == data) + { + IOLog("%s: Unexpected resend (%02x) request from PS/2 controller.\n", getName(), data); + return kPS2IR_packetBuffering; + } + + // + // See if this scan code introduces an extended key sequence. If so, note + // it and then return. Next time we get a key we'll finish the sequence. + // + + if (data == kSC_Extend) + { + _extendCount = 1; + return kPS2IR_packetBuffering; + } + + // + // See if this scan code introduces an extended key sequence for the Pause + // Key. If so, note it and then return. The next time we get a key, drop + // it. The next key we get after that finishes the Pause Key sequence. + // + // The sequence actually sent to us by the keyboard for the Pause Key is: + // + // 1. E1 Extended Sequence for Pause Key + // 2. 1D Useless Data, with Up Bit Cleared + // 3. 45 Pause Key, with Up Bit Cleared + // 4. E1 Extended Sequence for Pause Key + // 5. 9D Useless Data, with Up Bit Set + // 6. C5 Pause Key, with Up Bit Set + // + // The reason items 4 through 6 are sent with the Pause Key is because the + // keyboard hardware never generates a release code for the Pause Key and + // the designers are being smart about it. The sequence above translates + // to this parser as two separate events, as it should be -- one down key + // event and one up key event (for the Pause Key). + // + + if (data == kSC_Pause) + { + _extendCount = 2; + return kPS2IR_packetBuffering; + } + + // + // Otherwise it is a normal scan code, packetize it... + // + + UInt8 extended = _extendCount; + if (!_extendCount || 0 == --_extendCount) + { + // Update our key bit vector, which maintains the up/down status of all keys. + unsigned keyCodeRaw = (extended << 8) | (data & ~kSC_UpBit); + if (!(_PS2flags[keyCodeRaw] & kBreaklessKey)) + { + if (!(data & kSC_UpBit)) + { + if (KBV_IS_KEYDOWN(keyCodeRaw)) + return kPS2IR_packetBuffering; + KBV_KEYDOWN(keyCodeRaw); + } + else + { + KBV_KEYUP(keyCodeRaw); + } + } + // non-repeat make, or just break found, buffer it and dispatch + packet[0] = extended + 1; // packet[0] = 0 is special packet, so add one + packet[1] = data; + // mark packet with timestamp + clock_get_uptime((uint64_t*)(&packet[kPacketTimeOffset])); + _ringBuffer.advanceHead(kPacketLength); + return kPS2IR_packetReady; + } + return kPS2IR_packetBuffering; +} + +void ApplePS2Keyboard::packetReady() +{ + // empty the ring buffer, dispatching each packet... + // each packet is always two bytes, for simplicity... + while (_ringBuffer.count() >= kPacketLength) + { + UInt8* packet = _ringBuffer.tail(); + if (0x00 != packet[0]) + { + if (!_macroInversion || !invertMacros(packet)) + { + // normal packet + dispatchKeyboardEventWithPacket(packet); + } + } + else + { + // command/reset packet + ////initKeyboard(); + } + _ringBuffer.advanceTail(kPacketLength); + } +} + +bool ApplePS2Keyboard::compareMacro(const UInt8* buffer, const UInt8* data, int count) +{ + while (count--) + { + if (buffer[0] != data[0] || buffer[1] != data[1]) + return false; + buffer += kPacketLength; + data += kPacketKeyDataLength; + } + return true; +} + +bool ApplePS2Keyboard::invertMacros(const UInt8* packet) +{ + assert(_macroInversion); + + if (!_macroTimer || !_macroBuffer) + return false; + + if (_macroCurrent > 0) + { + // cancel macro conversion if packet arrives too late + uint64_t now_ns; + absolutetime_to_nanoseconds(*(uint64_t*)(&packet[kPacketTimeOffset]), &now_ns); + uint64_t prev; + absolutetime_to_nanoseconds(*(uint64_t*)(&_macroBuffer[(_macroCurrent-1)*kPacketLength+kPacketTimeOffset]), &prev); + if (now_ns-prev > _macroMaxTime) + dispatchInvertBuffer(); +#if 0 // for testing min/max between macro segments + uint64_t diff = now_ns-prev; + static uint64_t diffmin = UINT64_MAX, diffmax = 0; + if (diff > diffmax) diffmax = diff; + if (diff < diffmin) diffmin = diff; + IOLog("diffmin=%lld, diffmax=%lld\n", diffmin, diffmax); +#endif + } + + // add current packet to macro buffer for comparison + memcpy(_macroBuffer+_macroCurrent*kPacketLength, packet, kPacketLength); + int buffered = _macroCurrent+1; + int total = buffered*kPacketKeyDataLength; + // compare against macro inversions + for (OSData** p = _macroInversion; *p; p++) + { + int length = (*p)->getLength()-kPrefixBytes; + if (total <= length) + { + const UInt8* data = static_cast((*p)->getBytesNoCopy()); + if (compareMacro(_macroBuffer, data+kSequenceBytesOffset, buffered)) + { + if (total == length) + { + // get modifier mask/compare from macro definition + UInt16 mask = (static_cast(data[kModifierBytesOffset+0]) << 8) + data[kModifierBytesOffset+1]; + UInt16 compare = (static_cast(data[kModifierBytesOffset+2]) << 8) + data[kModifierBytesOffset+3]; + if ((0xFFFF == compare && (_PS2modifierState & mask)) || ((_PS2modifierState & mask) == compare)) + { + // exact match causes macro inversion + // grab bytes from macro definition + _macroBuffer[0] = data[kOutputBytesOffset+0]; + _macroBuffer[1] = data[kOutputBytesOffset+1]; + // dispatch constructed packet (timestamp is stamp on first macro packet) + dispatchKeyboardEventWithPacket(_macroBuffer); + cancelTimer(_macroTimer); + _macroCurrent = 0; + return true; + } + } + else + { + // partial match, keep waiting for full match + cancelTimer(_macroTimer); + setTimerTimeout(_macroTimer, _macroMaxTime); + _macroCurrent++; + return true; + } + } + } + } + // no match, so... empty macro buffer that may have been existing... + if (_macroCurrent > 0) + dispatchInvertBuffer(); + + return false; +} + +void ApplePS2Keyboard::onMacroTimer() +{ + DEBUG_LOG("ApplePS2Keyboard::onMacroTimer\n"); + + // timers have a very high priority, packets may have been placed in the + // input queue already. + + // by calling packetReady, these packets are processed before dealing with + // the timer (the packets may have arrived before the timer) + packetReady(); + + // after all packets have been processed, ok to check for time expiration + if (_macroCurrent > 0) + { + uint64_t now_abs, now_ns; + clock_get_uptime(&now_abs); + absolutetime_to_nanoseconds(now_abs, &now_ns); + uint64_t prev; + absolutetime_to_nanoseconds(*(uint64_t*)(&_macroBuffer[(_macroCurrent-1)*kPacketLength+kPacketTimeOffset]), &prev); + if (now_ns-prev > _macroMaxTime) + dispatchInvertBuffer(); + } +} + +void ApplePS2Keyboard::dispatchInvertBuffer() +{ + UInt8* packet = _macroBuffer; + for (int i = 0; i < _macroCurrent; i++) + { + // dispatch constructed packet + dispatchKeyboardEventWithPacket(packet); + packet += kPacketLength; + } + _macroCurrent = 0; + cancelTimer(_macroTimer); +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +// +// Note: attempted brightness through ACPI methods, but it didn't work. +// +// I think because Probook 4530s does some funny in things in its +// ACPI brightness methods. +// +// Just keeping it here in case someone wants to try with theirs. + +void ApplePS2Keyboard::modifyScreenBrightness(int adbKeyCode, bool goingDown) +{ + assert(_provider); + assert(_brightnessLevels); + + // get current brightness level + UInt32 result; + if (kIOReturnSuccess != _provider->evaluateInteger("KBQC", &result)) + { + DEBUG_LOG("ps2br: KBQC returned error\n"); + return; + } + int current = result; +#ifdef DEBUG_VERBOSE + if (goingDown) + DEBUG_LOG("ps2br: Current brightness: %d\n", current); +#endif + // calculate new brightness level, find current in table >= entry in table + // note first two entries in table are ac-power/battery + int index = 2; + while (index < _brightnessCount) + { + if (_brightnessLevels[index] >= current) + break; + ++index; + } + // move to next or previous + index += (adbKeyCode == 0x90 ? +1 : -1); + if (index >= _brightnessCount) + index = _brightnessCount - 1; + if (index < 2) + index = 2; +#ifdef DEBUG_VERBOSE + if (goingDown) + DEBUG_LOG("ps2br: setting brightness %d\n", _brightnessLevels[index]); +#endif + OSNumber* num = OSNumber::withNumber(_brightnessLevels[index], 32); + if (!num) + { + DEBUG_LOG("ps2br: OSNumber::withNumber failed\n"); + return; + } + if (goingDown && kIOReturnSuccess != _provider->evaluateObject("KBCM", NULL, (OSObject**)&num, 1)) + { + DEBUG_LOG("ps2br: KBCM returned error\n"); + } + num->release(); +} + +// +// Note: trying for ACPI backlight control for ASUS notebooks +// +// This one did work, so leaving this in. It is done in a generic way such that +// it can be used on more than just ASUS laptops, provided you can figure out +// how to implememnt the KKQC, KKCM, and KKCL methods. +// + +void ApplePS2Keyboard::modifyKeyboardBacklight(int keyCode, bool goingDown) +{ + assert(_provider); + assert(_backlightLevels); + + // get current brightness level + UInt32 result; + if (kIOReturnSuccess != _provider->evaluateInteger("KKQC", &result)) + { + DEBUG_LOG("ps2bl: KKQC returned error\n"); + return; + } + int current = result; +#ifdef DEBUG_VERBOSE + if (goingDown) + DEBUG_LOG("ps2bl: Current keyboard backlight: %d\n", current); +#endif + // calculate new brightness level, find current in table >= entry in table + // note first two entries in table are ac-power/battery + int index = 0; + while (index < _backlightCount) + { + if (_backlightLevels[index] >= current) + break; + ++index; + } + // move to next or previous + index += (keyCode == 0x4e ? +1 : -1); + if (index >= _backlightCount) + index = _backlightCount - 1; + if (index < 0) + index = 0; +#ifdef DEBUG_VERBOSE + if (goingDown) + DEBUG_LOG("ps2bl: setting keyboard backlight %d\n", _backlightLevels[index]); +#endif + OSNumber* num = OSNumber::withNumber(_backlightLevels[index], 32); + if (!num) + { + DEBUG_LOG("ps2bl: OSNumber::withNumber failed\n"); + return; + } + if (goingDown && kIOReturnSuccess != _provider->evaluateObject("KKCM", NULL, (OSObject**)&num, 1)) + { + DEBUG_LOG("ps2bl: KKCM returned error\n"); + } + num->release(); +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +void ApplePS2Keyboard::onSleepEjectTimer() +{ + switch (_timerFunc) + { + case kTimerSleep: + { + IOPMrootDomain* rootDomain = getPMRootDomain(); + if (NULL != rootDomain) + rootDomain->receivePowerNotification(kIOPMSleepNow); + break; + } + + case kTimerEject: + { + uint64_t now_abs; + clock_get_uptime(&now_abs); + dispatchKeyboardEventX(0x92, true, now_abs); + break; + } + } +} + +bool ApplePS2Keyboard::dispatchKeyboardEventWithPacket(const UInt8* packet) +{ + // Parses the given scan code, updating all necessary internal state, and + // should a new key be detected, the key event is dispatched. + // + // Returns true if a key event was indeed dispatched. + + UInt8 extended = packet[0] - 1; + UInt8 scanCode = packet[1]; + +#ifdef DEBUG_VERBOSE + DEBUG_LOG("%s: PS/2 scancode %s 0x%x\n", getName(), extended ? "extended" : "", scanCode); +#endif + + unsigned keyCodeRaw = scanCode & ~kSC_UpBit; + bool goingDown = !(scanCode & kSC_UpBit); + unsigned keyCode; + uint64_t now_abs = *(uint64_t*)(&packet[kPacketTimeOffset]); + uint64_t now_ns; + absolutetime_to_nanoseconds(now_abs, &now_ns); + + // + // Convert the scan code into a key code index. + // + // From "The Undocumented PC" chapter 8, The Keyboard System some + // keyboard scan codes are single byte, some are multi-byte. + // Scancodes from running showkey -s (under Linux) for extra keys on keyboard + // Refer to the conversion table in defaultKeymapOfLength + // and the conversion table in ApplePS2ToADBMap.h. + // + if (!extended) + { + // LANG1(Hangul) and LANG2(Hanja) make one event only when the key was pressed. + // Make key-down and key-up event ADB event + if (scanCode == 0xf2 || scanCode == 0xf1) + { + clock_get_uptime(&now_abs); + dispatchKeyboardEventX(_PS2ToADBMap[scanCode], true, now_abs); + clock_get_uptime(&now_abs); + dispatchKeyboardEventX(_PS2ToADBMap[scanCode], false, now_abs); + return true; + } + + // Allow PS2 -> PS2 map to work, look in normal part of the table + keyCode = _PS2ToPS2Map[keyCodeRaw]; + +#ifdef DEBUG_VERBOSE + if (keyCode != keyCodeRaw) + DEBUG_LOG("%s: keycode translated from=0x%02x to=0x%04x\n", getName(), keyCodeRaw, keyCode); +#endif + } + else + { + // allow PS2 -> PS2 map to work, look in extended part of the table + keyCodeRaw += KBV_NUM_SCANCODES; + keyCode = _PS2ToPS2Map[keyCodeRaw]; + +#ifdef DEBUG_VERBOSE + if (keyCode != keyCodeRaw) + DEBUG_LOG("%s: keycode translated from=0xe0%02x to=0x%04x\n", getName(), keyCodeRaw, keyCode); +#endif + // handle special cases + switch (keyCodeRaw) + { + case 0x012a: // header or trailer for PrintScreen + return false; + } + } + + // tracking modifier key state + if (UInt8 bit = (_PS2flags[keyCodeRaw] >> 8)) + { + UInt16 mask = 1 << (bit-1); + goingDown ? _PS2modifierState |= mask : _PS2modifierState &= ~mask; + } + + // codes e0f0 through e0ff can be used to call back into ACPI methods on this device + if (keyCode >= 0x01f0 && keyCode <= 0x01ff && _provider != NULL) + { + // evaluate RKA[0-F] for these keys + char method[5] = "RKAx"; + char n = keyCode - 0x01f0; + method[3] = n < 10 ? n + '0' : n - 10 + 'A'; + if (OSNumber* num = OSNumber::withNumber(goingDown, 32)) + { + // call ACPI RKAx(Arg0=goingDown) + _provider->evaluateObject(method, NULL, (OSObject**)&num, 1); + num->release(); + } + } + + // handle special cases + switch (keyCode) + { + case 0x4e: // Numpad+ + case 0x4a: // Numpad- + if (_backlightLevels && checkModifierState(kMaskLeftControl|kMaskLeftAlt)) + { + // Ctrl+Alt+Numpad(+/-) => use to manipulate keyboard backlight + modifyKeyboardBacklight(keyCode, goingDown); + keyCode = 0; + } + else if (_brightnessHack && checkModifierState(kMaskLeftControl|kMaskLeftShift)) + { + // Ctrl+Shift+NumPad(+/0) => manipulate brightness (special hack for HP Envy) + // Fn+F2 generates e0 ab and so does Fn+F3 (we will null those out in ps2 map) + static unsigned keys[] = { 0x2a, 0x1d }; + // if Option key is down don't pull up on the Shift keys + int start = checkModifierState(kMaskLeftWindows) ? 1 : 0; + for (int i = start; i < countof(keys); i++) + if (KBV_IS_KEYDOWN(keys[i])) + dispatchKeyboardEventX(_PS2ToADBMap[keys[i]], false, now_abs); + dispatchKeyboardEventX(keyCode == 0x4e ? 0x90 : 0x91, goingDown, now_abs); + for (int i = start; i < countof(keys); i++) + if (KBV_IS_KEYDOWN(keys[i])) + dispatchKeyboardEventX(_PS2ToADBMap[keys[i]], true, now_abs); + keyCode = 0; + } + break; + + case 0x0153: // delete + // check for Ctrl+Alt+Delete? (three finger salute) + if (checkModifierState(kMaskLeftControl|kMaskLeftAlt)) + { + keyCode = 0; + if (!goingDown) + { + // Note: If OS X thinks the Command and Control keys are down at the time of + // receiving an ADB 0x7f (power button), it will unconditionaly and unsafely + // reboot the computer, much like the old PC/AT Ctrl+Alt+Delete! + // That's why we make sure Control (0x3b) and Alt (0x37) are up!! + dispatchKeyboardEventX(0x37, false, now_abs); + dispatchKeyboardEventX(0x3b, false, now_abs); + dispatchKeyboardEventX(0x7f, true, now_abs); + dispatchKeyboardEventX(0x7f, false, now_abs); + } + } + break; + + case 0x015f: // sleep + keyCode = 0; + if (goingDown) + { + _timerFunc = kTimerSleep; + if (_fkeymode || !_maxsleeppresstime) + onSleepEjectTimer(); + else + setTimerTimeout(_sleepEjectTimer, (uint64_t)_maxsleeppresstime * 1000000); + } + else + { + cancelTimer(_sleepEjectTimer); + } + break; + + //REVIEW: this is getting a bit ugly + case 0x0128: // alternate that cannot fnkeys toggle (discrete trackpad toggle) + case 0x0137: // prt sc/sys rq + { + unsigned origKeyCode = keyCode; + keyCode = 0; + if (!goingDown) + break; + if (!checkModifierState(kMaskLeftControl)) + { + // get current enabled status, and toggle it + bool enabled; + _device->dispatchMouseMessage(kPS2M_getDisableTouchpad, &enabled); + enabled = !enabled; + _device->dispatchMouseMessage(kPS2M_setDisableTouchpad, &enabled); + break; + } + if (origKeyCode != 0x0137) + break; // do not fall through for 0x0128 + // fall through + } + case 0x0127: // alternate for fnkeys toggle (discrete fnkeys toggle) + keyCode = 0; + if (!goingDown) + break; + if (_fkeymodesupported) + { + // modify HIDFKeyMode via IOService... IOHIDSystem + if (IOService* service = IOService::waitForMatchingService(serviceMatching(kIOHIDSystem), 0)) + { + const OSObject* num = OSNumber::withNumber(!_fkeymode, 32); + const OSString* key = OSString::withCString(kHIDFKeyMode); + if (num && key) + { + if (OSDictionary* dict = OSDictionary::withObjects(&num, &key, 1)) + { + service->setProperties(dict); + dict->release(); + } + } + OSSafeReleaseNULL(num); + OSSafeReleaseNULL(key); + service->release(); + } + } + break; + } + +#ifdef DEBUG + // allow hold Alt+numpad keys to type in arbitrary ADB key code + static int genADB = -1; + if (goingDown && checkModifierState(kMaskLeftAlt) && + ((keyCodeRaw >= 0x47 && keyCodeRaw <= 0x52 && keyCodeRaw != 0x4e && keyCodeRaw != 0x4a) || + (keyCodeRaw >= 0x02 && keyCodeRaw <= 0x0B))) + { + // map numpad scan codes to digits + static int map1[0x52-0x47+1] = { 7, 8, 9, -1, 4, 5, 6, -1, 1, 2, 3, 0 }; + static int map2[0x0B-0x02+1] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 }; + if (-1 == genADB) + genADB = 0; + int digit = keyCodeRaw >= 0x47 ? map1[keyCodeRaw-0x47] : map2[keyCodeRaw-0x02]; + if (-1 != digit) + genADB = genADB * 10 + digit; + DEBUG_LOG("%s: genADB = %d\n", getName(), genADB); + keyCode = 0; // eat it + } +#endif + + // We have a valid key event -- dispatch it to our superclass. + + // map scan code to Apple code + UInt8 adbKeyCode = _PS2ToADBMap[keyCode]; + bool eatKey = false; + + // special cases + switch (adbKeyCode) + { + case 0x90: + case 0x91: + if (_brightnessLevels) + { + modifyScreenBrightness(adbKeyCode, goingDown); + adbKeyCode = DEADKEY; + } + break; + case 0x92: // eject + if (0 == _PS2modifierState) + { + if (goingDown) + { + eatKey = true; + _timerFunc = kTimerEject; + if (!_f12ejectdelay) + onSleepEjectTimer(); + else + setTimerTimeout(_sleepEjectTimer, (uint64_t)_f12ejectdelay * 1000000); + } + else + { + cancelTimer(_sleepEjectTimer); + } + } + break; + } + +#ifdef DEBUG_VERBOSE + if (adbKeyCode == DEADKEY && 0 != keyCode) + DEBUG_LOG("%s: Unknown ADB key for PS2 scancode: 0x%x\n", getName(), scanCode); + else + DEBUG_LOG("%s: ADB key code 0x%x %s\n", getName(), adbKeyCode, goingDown?"down":"up"); +#endif + +#ifdef DEBUG_LITE + int logscancodes = 2; +#else + int logscancodes = _logscancodes; +#endif + if (logscancodes==2 || (logscancodes==1 && goingDown)) + { + if (keyCode == keyCodeRaw) + IOLog("%s: sending key %x=%x %s\n", getName(), keyCode > KBV_NUM_SCANCODES ? (keyCode & 0xFF) | 0xe000 : keyCode, adbKeyCode, goingDown?"down":"up"); + else + IOLog("%s: sending key %x=%x, %x=%x %s\n", getName(), keyCodeRaw > KBV_NUM_SCANCODES ? (keyCodeRaw & 0xFF) | 0xe000 : keyCodeRaw, keyCode > KBV_NUM_SCANCODES ? (keyCode & 0xFF) | 0xe000 : keyCode, keyCode > KBV_NUM_SCANCODES ? (keyCode & 0xFF) | 0xe000 : keyCode, adbKeyCode, goingDown?"down":"up"); + } + + // allow mouse/trackpad driver to have time of last keyboard activity + // used to implement "PalmNoAction When Typing" and "OutsizeZoneNoAction When Typing" + PS2KeyInfo info; + info.time = now_ns; + info.adbKeyCode = adbKeyCode; + info.goingDown = goingDown; + info.eatKey = eatKey; + _device->dispatchMouseMessage(kPS2M_notifyKeyPressed, &info); + + if (adbKeyCode == 0x39 && version_major >= 16) + { + if (goingDown) + { + static bool firsttime = true; + clock_get_uptime(&now_abs); + dispatchKeyboardEventX(adbKeyCode, true, now_abs); + clock_get_uptime(&now_abs); + dispatchKeyboardEventX(adbKeyCode, false, now_abs); + if (!firsttime) + { + clock_get_uptime(&now_abs); + dispatchKeyboardEventX(adbKeyCode, true, now_abs); + clock_get_uptime(&now_abs); + dispatchKeyboardEventX(adbKeyCode, false, now_abs); + } + firsttime = false; + } + return true; + } + + if (keyCode && !info.eatKey) + { + // dispatch to HID system + if (goingDown || !(_PS2flags[keyCodeRaw] & kBreaklessKey)) + dispatchKeyboardEventX(adbKeyCode, goingDown, now_abs); + if (goingDown && (_PS2flags[keyCodeRaw] & kBreaklessKey)) + dispatchKeyboardEventX(adbKeyCode, false, now_abs); + } + +#ifdef DEBUG + if (0x38 == keyCode && !goingDown && -1 != genADB) // Alt going up + { + // dispatch typed adb code + dispatchKeyboardEventX(genADB, true, now_abs); + dispatchKeyboardEventX(genADB, false, now_abs); + DEBUG_LOG("%s: sending typed ADB code 0x%x\n", getName(), genADB); + genADB = -1; + } +#endif + + return true; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +void ApplePS2Keyboard::sendKeySequence(UInt16* pKeys) +{ + for (; *pKeys; ++pKeys) + { + uint64_t now_abs; + clock_get_uptime(&now_abs); + dispatchKeyboardEventX(*pKeys & 0xFF, *pKeys & 0x1000 ? false : true, now_abs); + } +} + +void ApplePS2Keyboard::receiveMessage(int message, void* data) +{ + // + // Here is where we receive messages from the mouse/trackpad driver + // + + switch (message) + { + case kPS2M_swipeDown: + DEBUG_LOG("ApplePS2Keyboard: Synaptic Trackpad call Swipe Down\n"); + sendKeySequence(_actionSwipeDown); + break; + + case kPS2M_swipeLeft: + DEBUG_LOG("ApplePS2Keyboard: Synaptic Trackpad call Swipe Left\n"); + sendKeySequence(_actionSwipeLeft); + break; + + case kPS2M_swipeRight: + DEBUG_LOG("ApplePS2Keyboard: Synaptic Trackpad call Swipe Right\n"); + sendKeySequence(_actionSwipeRight); + break; + + case kPS2M_swipeUp: + DEBUG_LOG("ApplePS2Keyboard: Synaptic Trackpad call Swipe Up\n"); + sendKeySequence(_actionSwipeUp); + break; + + case kPS2M_swipe4Down: + DEBUG_LOG("ApplePS2Keyboard: Synaptic Trackpad call Swipe 4 Down\n"); + sendKeySequence(_actionSwipe4Down); + break; + + case kPS2M_swipe4Left: + DEBUG_LOG("ApplePS2Keyboard: Synaptic Trackpad call Swipe 4 Left\n"); + sendKeySequence(_actionSwipe4Left); + break; + + case kPS2M_swipe4Right: + DEBUG_LOG("ApplePS2Keyboard: Synaptic Trackpad call Swipe 4 Right\n"); + sendKeySequence(_actionSwipe4Right); + break; + + case kPS2M_swipe4Up: + DEBUG_LOG("ApplePS2Keyboard: Synaptic Trackpad call Swipe 4 Up\n"); + sendKeySequence(_actionSwipe4Up); + break; + } +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +void ApplePS2Keyboard::setAlphaLockFeedback(bool locked) +{ + // + // Set the keyboard LEDs to reflect the state of alpha (caps) lock. + // + // It is safe to issue this request from the interrupt/completion context. + // + + _ledState = locked ? (_ledState | kLED_CapsLock):(_ledState & ~kLED_CapsLock); + setLEDs(_ledState); +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +void ApplePS2Keyboard::setNumLockFeedback(bool locked) +{ + // + // Set the keyboard LEDs to reflect the state of num lock. + // + // It is safe to issue this request from the interrupt/completion context. + // + + _ledState = locked ? (_ledState | kLED_NumLock):(_ledState & ~kLED_NumLock); + setLEDs(_ledState); +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +void ApplePS2Keyboard::setLEDs(UInt8 ledState) +{ + // + // Asynchronously instructs the controller to set the keyboard LED state. + // + // It is safe to issue this request from the interrupt/completion context. + // + + PS2Request* request = _device->allocateRequest(4); + + // (set LEDs command) + request->commands[0].command = kPS2C_WriteDataPort; + request->commands[0].inOrOut = kDP_SetKeyboardLEDs; + request->commands[1].command = kPS2C_ReadDataPortAndCompare; + request->commands[1].inOrOut = kSC_Acknowledge; + request->commands[2].command = kPS2C_WriteDataPort; + request->commands[2].inOrOut = ledState; + request->commands[3].command = kPS2C_ReadDataPortAndCompare; + request->commands[3].inOrOut = kSC_Acknowledge; + request->commandsCount = 4; + _device->submitRequest(request); +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +void ApplePS2Keyboard::setKeyboardEnable(bool enable) +{ + // + // Instructs the keyboard to start or stop the reporting of key events. + // Be aware that while the keyboard is enabled, asynchronous key events + // may arrive in the middle of command sequences sent to the controller, + // and may get confused for expected command responses. + // + // It is safe to issue this request from the interrupt/completion context. + // + + // (keyboard enable/disable command) + TPS2Request<2> request; + request.commands[0].command = kPS2C_WriteDataPort; + request.commands[0].inOrOut = enable ? kDP_Enable : kDP_SetDefaultsAndDisable; + request.commands[1].command = kPS2C_ReadDataPortAndCompare; + request.commands[1].inOrOut = kSC_Acknowledge; + request.commandsCount = 2; + assert(request.commandsCount <= countof(request.commands)); + _device->submitRequestAndBlock(&request); +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +const unsigned char * ApplePS2Keyboard::defaultKeymapOfLength(UInt32 * length) +{ + // + // Keymap data borrowed and modified from IOHIDFamily/IOHIDKeyboard.cpp + // references http://www.xfree.org/current/dumpkeymap.1.html + // http://www.tamasoft.co.jp/en/general-info/unicode.html + // + static const unsigned char appleUSAKeyMap[] = { + 0x00,0x00, // use byte unit. + + + // modifier definition + 0x0b, //Number of modifier keys. + // ( modifier , num of keys, ADB keycodes... ) + // ( 0x00 , 0x01 , 0x39 ) + //0x00,0x01,0x39, //NX_MODIFIERKEY_ALPHALOCK, uses one byte, ADB keycode is 0x39 + NX_MODIFIERKEY_SHIFT, 0x01,0x38, + NX_MODIFIERKEY_CONTROL, 0x01,0x3b, + NX_MODIFIERKEY_ALTERNATE, 0x01,0x3a, + NX_MODIFIERKEY_COMMAND, 0x01,0x37, + NX_MODIFIERKEY_NUMERICPAD, 0x15,0x52,0x41,0x4c,0x53,0x54,0x55,0x45,0x58,0x57,0x56,0x5b,0x5c,0x43,0x4b,0x51,0x7b,0x7d,0x7e,0x7c,0x4e,0x59, + NX_MODIFIERKEY_HELP, 0x01,0x72, + NX_MODIFIERKEY_SECONDARYFN, 0x01,0x3f, // Apple's Fn key + NX_MODIFIERKEY_RSHIFT, 0x01,0x3c, + NX_MODIFIERKEY_RCONTROL, 0x01,0x3e, + NX_MODIFIERKEY_RALTERNATE, 0x01,0x3d, + NX_MODIFIERKEY_RCOMMAND, 0x01,0x36, + + + // ADB virtual key definitions + 0xa2, // number of key definitions + // ( modifier mask , generated character{char_set,char_code}... ) + // ( 0x0d[has 3bit modifiers], {0x00,0x3c}, {0x00,0x3e}, ... total 2^3 characters ) + 0x0d,0x00,0x61,0x00,0x41,0x00,0x01,0x00,0x01,0x00,0xca,0x00,0xc7,0x00,0x01,0x00,0x01, //00 A + 0x0d,0x00,0x73,0x00,0x53,0x00,0x13,0x00,0x13,0x00,0xfb,0x00,0xa7,0x00,0x13,0x00,0x13, //01 S + 0x0d,0x00,0x64,0x00,0x44,0x00,0x04,0x00,0x04,0x01,0x44,0x01,0xb6,0x00,0x04,0x00,0x04, //02 D + 0x0d,0x00,0x66,0x00,0x46,0x00,0x06,0x00,0x06,0x00,0xa6,0x01,0xac,0x00,0x06,0x00,0x06, //03 F + 0x0d,0x00,0x68,0x00,0x48,0x00,0x08,0x00,0x08,0x00,0xe3,0x00,0xeb,0x00,0x00,0x18,0x00, //04 H + 0x0d,0x00,0x67,0x00,0x47,0x00,0x07,0x00,0x07,0x00,0xf1,0x00,0xe1,0x00,0x07,0x00,0x07, //05 G + 0x0d,0x00,0x7a,0x00,0x5a,0x00,0x1a,0x00,0x1a,0x00,0xcf,0x01,0x57,0x00,0x1a,0x00,0x1a, //06 Z + 0x0d,0x00,0x78,0x00,0x58,0x00,0x18,0x00,0x18,0x01,0xb4,0x01,0xce,0x00,0x18,0x00,0x18, //07 X + 0x0d,0x00,0x63,0x00,0x43,0x00,0x03,0x00,0x03,0x01,0xe3,0x01,0xd3,0x00,0x03,0x00,0x03, //08 C + 0x0d,0x00,0x76,0x00,0x56,0x00,0x16,0x00,0x16,0x01,0xd6,0x01,0xe0,0x00,0x16,0x00,0x16, //09 V + 0x02,0x00,0x3c,0x00,0x3e, //0a NON-US-BACKSLASH on ANSI and JIS keyboards GRAVE on ISO + 0x0d,0x00,0x62,0x00,0x42,0x00,0x02,0x00,0x02,0x01,0xe5,0x01,0xf2,0x00,0x02,0x00,0x02, //0b B + 0x0d,0x00,0x71,0x00,0x51,0x00,0x11,0x00,0x11,0x00,0xfa,0x00,0xea,0x00,0x11,0x00,0x11, //0c Q + 0x0d,0x00,0x77,0x00,0x57,0x00,0x17,0x00,0x17,0x01,0xc8,0x01,0xc7,0x00,0x17,0x00,0x17, //0d W + 0x0d,0x00,0x65,0x00,0x45,0x00,0x05,0x00,0x05,0x00,0xc2,0x00,0xc5,0x00,0x05,0x00,0x05, //0e E + 0x0d,0x00,0x72,0x00,0x52,0x00,0x12,0x00,0x12,0x01,0xe2,0x01,0xd2,0x00,0x12,0x00,0x12, //0f R + 0x0d,0x00,0x79,0x00,0x59,0x00,0x19,0x00,0x19,0x00,0xa5,0x01,0xdb,0x00,0x19,0x00,0x19, //10 Y + 0x0d,0x00,0x74,0x00,0x54,0x00,0x14,0x00,0x14,0x01,0xe4,0x01,0xd4,0x00,0x14,0x00,0x14, //11 T + 0x0a,0x00,0x31,0x00,0x21,0x01,0xad,0x00,0xa1, //12 1 + 0x0e,0x00,0x32,0x00,0x40,0x00,0x32,0x00,0x00,0x00,0xb2,0x00,0xb3,0x00,0x00,0x00,0x00, //13 2 + 0x0a,0x00,0x33,0x00,0x23,0x00,0xa3,0x01,0xba, //14 3 + 0x0a,0x00,0x34,0x00,0x24,0x00,0xa2,0x00,0xa8, //15 4 + 0x0e,0x00,0x36,0x00,0x5e,0x00,0x36,0x00,0x1e,0x00,0xb6,0x00,0xc3,0x00,0x1e,0x00,0x1e, //16 6 + 0x0a,0x00,0x35,0x00,0x25,0x01,0xa5,0x00,0xbd, //17 5 + 0x0a,0x00,0x3d,0x00,0x2b,0x01,0xb9,0x01,0xb1, //18 EQUALS + 0x0a,0x00,0x39,0x00,0x28,0x00,0xac,0x00,0xab, //19 9 + 0x0a,0x00,0x37,0x00,0x26,0x01,0xb0,0x01,0xab, //1a 7 + 0x0e,0x00,0x2d,0x00,0x5f,0x00,0x1f,0x00,0x1f,0x00,0xb1,0x00,0xd0,0x00,0x1f,0x00,0x1f, //1b MINUS + 0x0a,0x00,0x38,0x00,0x2a,0x00,0xb7,0x00,0xb4, //1c 8 + 0x0a,0x00,0x30,0x00,0x29,0x00,0xad,0x00,0xbb, //1d 0 + 0x0e,0x00,0x5d,0x00,0x7d,0x00,0x1d,0x00,0x1d,0x00,0x27,0x00,0xba,0x00,0x1d,0x00,0x1d, //1e RIGHTBRACKET + 0x0d,0x00,0x6f,0x00,0x4f,0x00,0x0f,0x00,0x0f,0x00,0xf9,0x00,0xe9,0x00,0x0f,0x00,0x0f, //1f O + 0x0d,0x00,0x75,0x00,0x55,0x00,0x15,0x00,0x15,0x00,0xc8,0x00,0xcd,0x00,0x15,0x00,0x15, //20 U + 0x0e,0x00,0x5b,0x00,0x7b,0x00,0x1b,0x00,0x1b,0x00,0x60,0x00,0xaa,0x00,0x1b,0x00,0x1b, //21 LEFTBRACKET + 0x0d,0x00,0x69,0x00,0x49,0x00,0x09,0x00,0x09,0x00,0xc1,0x00,0xf5,0x00,0x09,0x00,0x09, //22 I + 0x0d,0x00,0x70,0x00,0x50,0x00,0x10,0x00,0x10,0x01,0x70,0x01,0x50,0x00,0x10,0x00,0x10, //23 P + 0x10,0x00,0x0d,0x00,0x03, //24 RETURN + 0x0d,0x00,0x6c,0x00,0x4c,0x00,0x0c,0x00,0x0c,0x00,0xf8,0x00,0xe8,0x00,0x0c,0x00,0x0c, //25 L + 0x0d,0x00,0x6a,0x00,0x4a,0x00,0x0a,0x00,0x0a,0x00,0xc6,0x00,0xae,0x00,0x0a,0x00,0x0a, //26 J + 0x0a,0x00,0x27,0x00,0x22,0x00,0xa9,0x01,0xae, //27 APOSTROPHE + 0x0d,0x00,0x6b,0x00,0x4b,0x00,0x0b,0x00,0x0b,0x00,0xce,0x00,0xaf,0x00,0x0b,0x00,0x0b, //28 K + 0x0a,0x00,0x3b,0x00,0x3a,0x01,0xb2,0x01,0xa2, //29 SEMICOLON + 0x0e,0x00,0x5c,0x00,0x7c,0x00,0x1c,0x00,0x1c,0x00,0xe3,0x00,0xeb,0x00,0x1c,0x00,0x1c, //2a BACKSLASH + 0x0a,0x00,0x2c,0x00,0x3c,0x00,0xcb,0x01,0xa3, //2b COMMA + 0x0a,0x00,0x2f,0x00,0x3f,0x01,0xb8,0x00,0xbf, //2c SLASH + 0x0d,0x00,0x6e,0x00,0x4e,0x00,0x0e,0x00,0x0e,0x00,0xc4,0x01,0xaf,0x00,0x0e,0x00,0x0e, //2d N + 0x0d,0x00,0x6d,0x00,0x4d,0x00,0x0d,0x00,0x0d,0x01,0x6d,0x01,0xd8,0x00,0x0d,0x00,0x0d, //2e M + 0x0a,0x00,0x2e,0x00,0x3e,0x00,0xbc,0x01,0xb3, //2f PERIOD + 0x02,0x00,0x09,0x00,0x19, //30 TAB + 0x0c,0x00,0x20,0x00,0x00,0x00,0x80,0x00,0x00, //31 SPACE + 0x0a,0x00,0x60,0x00,0x7e,0x00,0x60,0x01,0xbb, //32 GRAVE on ANSI and JIS keyboards, NON-US-BACKSLASH on ISO + 0x02,0x00,0x7f,0x00,0x08, //33 BACKSPACE + 0xff, //34 PLAY/PAUSE + 0x02,0x00,0x1b,0x00,0x7e, //35 ESCAPE + 0xff, //36 RGUI + 0xff, //37 LGUI + 0xff, //38 LSHIFT + 0xff, //39 CAPSLOCK + 0xff, //3a LALT + 0xff, //3b LCTRL + 0xff, //3c RSHIFT + 0xff, //3d RALT + 0xff, //3e RCTRL + 0xff, //3f Apple Fn key + 0x00,0xfe,0x36, //40 F17 + 0x00,0x00,0x2e, //41 KEYPAD_PERIOD + 0xff, //42 NEXT TRACK or FAST + 0x00,0x00,0x2a, //43 KEYPAD_MULTIPLY + 0xff, //44 + 0x00,0x00,0x2b, //45 KEYPAD_PLUS + 0xff, //46 + 0x00,0x00,0x1b, //47 CLEAR + 0xff, //48 VOLUME UP + 0xff, //49 VOLUME DOWN + 0xff, //4a MUTE + 0x0e,0x00,0x2f,0x00,0x5c,0x00,0x2f,0x00,0x1c,0x00,0x2f,0x00,0x5c,0x00,0x00,0x0a,0x00, //4b KEYPAD_DIVIDE + 0x00,0x00,0x0d, //4c Apple Fn + Return = ENTER //XX03 + 0xff, //4d PREVIOUS TRACK or REWIND + 0x00,0x00,0x2d, //4e KEYPAD_MINUS + 0x00,0xfe,0x37, //4f F18 + 0x00,0xfe,0x38, //50 F19 + 0x0e,0x00,0x3d,0x00,0x7c,0x00,0x3d,0x00,0x1c,0x00,0x3d,0x00,0x7c,0x00,0x00,0x18,0x46, //51 KEYPAD_EQUALS + 0x00,0x00,0x30, //52 KEYPAD_0 + 0x00,0x00,0x31, //53 KEYPAD_1 + 0x00,0x00,0x32, //54 KEYPAD_2 + 0x00,0x00,0x33, //55 KEYPAD_3 + 0x00,0x00,0x34, //56 KEYPAD_4 + 0x00,0x00,0x35, //57 KEYPAD_5 + 0x00,0x00,0x36, //58 KEYPAD_6 + 0x00,0x00,0x37, //59 KEYPAD_7 + 0x00,0xfe,0x39, //5a F20 + 0x00,0x00,0x38, //5b KEYPAD_8 + 0x00,0x00,0x39, //5c KEYPAD_9 + 0xff, //0x02,0x00,0xa5,0x00,0x7c, //5d JIS JAPANESE YEN + 0xff, //0x00,0x00,0x5f, //5e JIS JAPANESE RO + 0xff, //0x00,0x00,0x2c, //5f KEYPAD_COMMA, JIS only + 0x00,0xfe,0x24, //60 F5 + 0x00,0xfe,0x25, //61 F6 + 0x00,0xfe,0x26, //62 F7 + 0x00,0xfe,0x22, //63 F3 + 0x00,0xfe,0x27, //64 F8 + 0x00,0xfe,0x28, //65 F9 + 0xff, //66 JIS JAPANESE EISU, KOREAN HANJA + 0x00,0xfe,0x2a, //67 F11 + 0xff, //68 JIS JAPANESE KANA, KOREAN HANGUL + 0x00,0xfe,0x32, //69 F13 + 0x00,0xfe,0x35, //6a F16 + 0x00,0xfe,0x33, //6b F14 + 0xff, //6c + 0x00,0xfe,0x29, //6d F10 + 0xff, //6e + 0x00,0xfe,0x2b, //6f F12 + 0xff, //70 + 0x00,0xfe,0x34, //71 F15 + 0xff, //72 HELP + 0x00,0xfe,0x2e, //73 HOME + 0x00,0xfe,0x30, //74 PAGEUP + 0x00,0xfe,0x2d, //75 DELETE + 0x00,0xfe,0x23, //76 F4 + 0x00,0xfe,0x2f, //77 END + 0x00,0xfe,0x21, //78 F2 + 0x00,0xfe,0x31, //79 PAGEDOWN + 0x00,0xfe,0x20, //7a F1 + 0x00,0x01,0xac, //7b LEFT ARROW + 0x00,0x01,0xae, //7c RIGHT ARROW + 0x00,0x01,0xaf, //7d DOWN ARROW + 0x00,0x01,0xad, //7e UP ARROW + 0x00,0x00,0x00, //7f POWER + 0x00,0x00,0x00, + 0x00,0x00,0x00, //81 Spotlight + 0x00,0x00,0x00, //82 Dashboard + 0x00,0x00,0x00, //83 Launchpad + 0x00,0x00,0x00, + 0x00,0x00,0x00, + 0x00,0x00,0x00, + 0x00,0x00,0x00, + 0x00,0x00,0x00, + 0x00,0x00,0x00, + 0x00,0x00,0x00, + 0x00,0x00,0x00, + 0x00,0x00,0x00, + 0x00,0x00,0x00, + 0x00,0x00,0x00, + 0x00,0x00,0x00, + 0x00,0x00,0x00, //90 Main Brightness Up + 0x00,0x00,0x00, //91 Main Brightness Down + 0x00,0x00,0x00, + 0x00,0x00,0x00, + 0x00,0x00,0x00, + 0x00,0x00,0x00, + 0x00,0x00,0x00, + 0x00,0x00,0x00, + 0x00,0x00,0x00, + 0x00,0x00,0x00, + 0x00,0x00,0x00, + 0x00,0x00,0x00, + 0x00,0x00,0x00, + 0x00,0x00,0x00, + 0x00,0x00,0x00, + 0x00,0x00,0x00, + 0x00,0x00,0x00, // a0 Exposes All + 0x00,0x00,0x00, // a1 Expose Desktop + + + // key sequence definition + // I tested some key sequence "Command + Shift + '['", but it doesn't work well. + // No one sequence was used on key deff table now. + 0x11, // number of of sequence definitions + // ( num of keys, generated sequence characters(char_set,char_code)... ) + // ( 0x02 , {0xff,0x04}, {0x00,0x31}, ) + 0x02,0xff,0x04,0x00,0x31, // Command + '1' + 0x02,0xff,0x04,0x00,0x32, // Command + '2' + 0x02,0xff,0x04,0x00,0x33, // Command + '3' + 0x02,0xff,0x04,0x00,0x34, // Command + '4' + 0x02,0xff,0x04,0x00,0x35, // Command + '5' + 0x02,0xff,0x04,0x00,0x36, // Command + '6' + 0x02,0xff,0x04,0x00,0x37, // Command + '7' + 0x02,0xff,0x04,0x00,0x38, // Command + '8' + 0x02,0xff,0x04,0x00,0x39, // Command + '9' + 0x02,0xff,0x04,0x00,0x30, // Command + '0' + 0x02,0xff,0x04,0x00,0x2d, // Command + '-' + 0x02,0xff,0x04,0x00,0x3d, // Command + '=' + 0x02,0xff,0x04,0x00,0x70, // Command + 'p' + 0x02,0xff,0x04,0x00,0x5d, // Command + ']' + 0x02,0xff,0x04,0x00,0x5b, // Command + '[' + 0x03,0xff,0x04,0xff,0x01,0x00,0x5b, // Command + Shift + '[' + 0x03,0xff,0x04,0xff,0x01,0x00,0x5d, // Command + shift + ']' + + + // special key definition + 0x0e, // number of special keys + // ( NX_KEYTYPE, Virtual ADB code ) + NX_KEYTYPE_CAPS_LOCK, 0x39, + NX_KEYTYPE_HELP, 0x72, + NX_POWER_KEY, 0x7f, + NX_KEYTYPE_MUTE, 0x4a, + NX_KEYTYPE_SOUND_UP, 0x48, + NX_KEYTYPE_SOUND_DOWN, 0x49, + // remove arrow keys as special keys. They are generating double up/down scroll events + // in both carbon and coco apps. + //NX_UP_ARROW_KEY, 0x7e, // ADB is 3e raw, 7e virtual (KMAP) + //NX_DOWN_ARROW_KEY, 0x7d, // ADB is 0x3d raw, 7d virtual + NX_KEYTYPE_NUM_LOCK, 0x47, // ADB combines with CLEAR key for numlock + NX_KEYTYPE_VIDMIRROR, 0x70, + NX_KEYTYPE_PLAY, 0x34, + NX_KEYTYPE_NEXT, 0x42, // if this event repeated, act as NX_KEYTYPE_FAST + NX_KEYTYPE_PREVIOUS, 0x4d, // if this event repeated, act as NX_KEYTYPE_REWIND + NX_KEYTYPE_BRIGHTNESS_UP, 0x90, + NX_KEYTYPE_BRIGHTNESS_DOWN, 0x91, + NX_KEYTYPE_EJECT, 0x92, + }; + + *length = sizeof(appleUSAKeyMap); + return appleUSAKeyMap; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +void ApplePS2Keyboard::setDevicePowerState( UInt32 whatToDo ) +{ + switch ( whatToDo ) + { + case kPS2C_DisableDevice: + // + // Disable keyboard. + // + setKeyboardEnable( false ); + break; + + case kPS2C_EnableDevice: + // + // Enable keyboard and restore state. + // + initKeyboard(); + break; + } +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +void ApplePS2Keyboard::initKeyboard() +{ + // + // Reset the keyboard to its default state. + // + + TPS2Request<2> request; + request.commands[0].command = kPS2C_WriteDataPort; + request.commands[0].inOrOut = kDP_SetDefaults; + request.commands[1].command = kPS2C_ReadDataPortAndCompare; + request.commands[1].inOrOut = kSC_Acknowledge; + request.commandsCount = 2; + assert(request.commandsCount <= countof(request.commands)); + _device->submitRequestAndBlock(&request); + + // look for any keys that are down (just in case the reset happened with keys down) + // for each key that is down, dispatch a key up for it + UInt8 packet[kPacketLength]; + for (int scanCode = 0; scanCode < KBV_NUM_KEYCODES; scanCode++) + { + if (KBV_IS_KEYDOWN(scanCode)) + { + packet[0] = scanCode < KBV_NUM_SCANCODES ? 1 : 2; + packet[1] = scanCode | kSC_UpBit; + dispatchKeyboardEventWithPacket(packet); + } + } + + // start out with all keys up + bzero(_keyBitVector, sizeof(_keyBitVector)); + _PS2modifierState = 0; + + // + // Initialize the keyboard LED state. + // + + setLEDs(_ledState); + + // + // Reset state of packet/keystroke buffer + // + + _extendCount = 0; + _ringBuffer.reset(); + + // + // Finally, we enable the keyboard itself, so that it may start reporting + // key events. + // + + setKeyboardEnable(true); + + // + // Enable keyboard Kscan -> scan code translation mode. + // + _device->setCommandByte(kCB_TranslateMode, 0); +} + diff --git a/VoodooPS2Keyboard/VoodooPS2Keyboard.h b/VoodooPS2Keyboard/VoodooPS2Keyboard.h new file mode 100644 index 0000000..63960ec --- /dev/null +++ b/VoodooPS2Keyboard/VoodooPS2Keyboard.h @@ -0,0 +1,194 @@ +/* + * Copyright (c) 1998-2000 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * The contents of this file constitute Original Code as defined in and + * are subject to the Apple Public Source License Version 1.1 (the + * "License"). You may not use this file except in compliance with the + * License. Please obtain a copy of the License at + * http://www.apple.com/publicsource and read it before using this file. + * + * This Original Code and all software distributed under the License are + * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the + * License for the specific language governing rights and limitations + * under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +#ifndef _APPLEPS2KEYBOARD_H +#define _APPLEPS2KEYBOARD_H + +#include +#include "ApplePS2KeyboardDevice.h" +#include +#include +#include + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +// Definitions used to keep track of key state. Key up/down state is tracked +// in a bit list. Bits are set for key-down, and cleared for key-up. The bit +// vector and macros for it's manipulation are defined here. +// + +#define KBV_NUM_KEYCODES 512 // related with ADB_CONVERTER_LEN +#define KBV_BITS_PER_UNIT 32 // for UInt32 +#define KBV_BITS_MASK 31 +#define KBV_BITS_SHIFT 5 // 1<<5 == 32, for cheap divide +#define KBV_NUNITS ((KBV_NUM_KEYCODES + \ + (KBV_BITS_PER_UNIT-1))/KBV_BITS_PER_UNIT) + +#define KBV_KEYDOWN(n) \ + (_keyBitVector)[((n)>>KBV_BITS_SHIFT)] |= (1 << ((n) & KBV_BITS_MASK)) + +#define KBV_KEYUP(n) \ + (_keyBitVector)[((n)>>KBV_BITS_SHIFT)] &= ~(1 << ((n) & KBV_BITS_MASK)) + +#define KBV_IS_KEYDOWN(n) \ + (((_keyBitVector)[((n)>>KBV_BITS_SHIFT)] & (1 << ((n) & KBV_BITS_MASK))) != 0) + +#define KBV_NUM_SCANCODES 256 + +// Special bits for _PS2ToPS2Map + +#define kBreaklessKey 0x01 // keys with this flag don't generate break codes + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +// ApplePS2Keyboard Class Declaration +// + +#define kPacketLength (2+6+8) // 2 bytes for key data, 6-bytes not used, 8 bytes for timestamp +#define kPacketKeyOffset 0 +#define kPacketTimeOffset 8 +#define kPacketKeyDataLength 2 + +class EXPORT ApplePS2Keyboard : public IOHIKeyboard +{ + typedef IOHIKeyboard super; + OSDeclareDefaultStructors(ApplePS2Keyboard); + +private: + ApplePS2KeyboardDevice * _device; + UInt32 _keyBitVector[KBV_NUNITS]; + UInt8 _extendCount; + RingBuffer _ringBuffer; + UInt8 _lastdata; + bool _interruptHandlerInstalled; + bool _powerControlHandlerInstalled; + bool _messageHandlerInstalled; + UInt8 _ledState; + IOCommandGate* _cmdGate; + + // for keyboard remapping + UInt16 _PS2modifierState; + UInt16 _PS2ToPS2Map[KBV_NUM_SCANCODES*2]; + UInt16 _PS2flags[KBV_NUM_SCANCODES*2]; + UInt8 _PS2ToADBMap[ADB_CONVERTER_LEN]; + UInt8 _PS2ToADBMapMapped[ADB_CONVERTER_LEN]; + UInt32 _fkeymode; + bool _fkeymodesupported; + OSArray* _keysStandard; + OSArray* _keysSpecial; + bool _swapcommandoption; + int _logscancodes; + UInt32 _f12ejectdelay; + enum { kTimerSleep, kTimerEject } _timerFunc; + + // dealing with sleep key delay + IOTimerEventSource* _sleepEjectTimer; + UInt32 _maxsleeppresstime; + + // configuration items for swipe actions + UInt16 _actionSwipeUp[16]; + UInt16 _actionSwipeDown[16]; + UInt16 _actionSwipeLeft[16]; + UInt16 _actionSwipeRight[16]; + UInt16 _actionSwipe4Up[16]; + UInt16 _actionSwipe4Down[16]; + UInt16 _actionSwipe4Left[16]; + UInt16 _actionSwipe4Right[16]; + + // ACPI support for screen brightness + IOACPIPlatformDevice * _provider; + int * _brightnessLevels; + int _brightnessCount; + + // ACPI support for keyboard backlight + int * _backlightLevels; + int _backlightCount; + + // special hack for Envy brightness access, while retaining F2/F3 functionality + bool _brightnessHack; + + // macro processing + OSData** _macroTranslation; + OSData** _macroInversion; + UInt8* _macroBuffer; + int _macroMax; + int _macroCurrent; + uint64_t _macroMaxTime; + IOTimerEventSource* _macroTimer; + + virtual bool dispatchKeyboardEventWithPacket(const UInt8* packet); + virtual void setLEDs(UInt8 ledState); + virtual void setKeyboardEnable(bool enable); + virtual void initKeyboard(); + virtual void setDevicePowerState(UInt32 whatToDo); + void sendKeySequence(UInt16* pKeys); + void modifyKeyboardBacklight(int adbKeyCode, bool goingDown); + void modifyScreenBrightness(int adbKeyCode, bool goingDown); + inline bool checkModifierState(UInt16 mask) + { return mask == (_PS2modifierState & mask); } + + void loadCustomPS2Map(OSArray* pArray); + void loadBreaklessPS2(OSDictionary* dict, const char* name); + void loadCustomADBMap(OSDictionary* dict, const char* name); + void setParamPropertiesGated(OSDictionary* dict); + void onSleepEjectTimer(void); + + static OSData** loadMacroData(OSDictionary* dict, const char* name); + static void freeMacroData(OSData** data); + void onMacroTimer(void); + bool invertMacros(const UInt8* packet); + void dispatchInvertBuffer(); + static bool compareMacro(const UInt8* packet, const UInt8* data, int count); + +protected: + virtual const unsigned char * defaultKeymapOfLength(UInt32 * length); + virtual void setAlphaLockFeedback(bool locked); + virtual void setNumLockFeedback(bool locked); + virtual UInt32 maxKeyCodes(); + inline void dispatchKeyboardEventX(unsigned int keyCode, bool goingDown, uint64_t time) + { dispatchKeyboardEvent(keyCode, goingDown, *(AbsoluteTime*)&time); } + inline void setTimerTimeout(IOTimerEventSource* timer, uint64_t time) + { timer->setTimeout(*(AbsoluteTime*)&time); } + inline void cancelTimer(IOTimerEventSource* timer) + { timer->cancelTimeout(); } + +public: + virtual bool init(OSDictionary * dict); + virtual void free(); + virtual ApplePS2Keyboard * probe(IOService * provider, SInt32 * score); + + virtual bool start(IOService * provider); + virtual void stop(IOService * provider); + + virtual PS2InterruptResult interruptOccurred(UInt8 scanCode); + virtual void packetReady(); + + virtual void receiveMessage(int message, void* data); + + virtual UInt32 deviceType(); + virtual UInt32 interfaceID(); + + virtual IOReturn setParamProperties(OSDictionary* dict); + virtual IOReturn setProperties (OSObject *props); + + virtual IOReturn message(UInt32 type, IOService* provider, void* argument); +}; + +#endif /* _APPLEPS2KEYBOARD_H */ diff --git a/VoodooPS2Keyboard/en.lproj/InfoPlist.strings b/VoodooPS2Keyboard/en.lproj/InfoPlist.strings new file mode 100644 index 0000000..b92732c --- /dev/null +++ b/VoodooPS2Keyboard/en.lproj/InfoPlist.strings @@ -0,0 +1 @@ +/* Localized versions of Info.plist keys */ diff --git a/VoodooPS2Trackpad/Decay.h b/VoodooPS2Trackpad/Decay.h new file mode 100644 index 0000000..03f2ece --- /dev/null +++ b/VoodooPS2Trackpad/Decay.h @@ -0,0 +1,135 @@ +// +// Decay.h +// VoodooPS2Controller +// +// Created by Brandon Pedersen on 5/4/13. +// Copyright (c) 2013 rehabman. All rights reserved. +// + +#ifndef VoodooPS2Controller_Decay_h +#define VoodooPS2Controller_Decay_h + +template +class SimpleAverage +{ +private: + T m_buffer[N]; + int m_count; + int m_sum; + int m_index; + +public: + inline SimpleAverage() { reset(); } + T filter(T data) + { + // add new entry to sum + m_sum += data; + // if full buffer, then we are overwriting, so subtract old from sum + if (m_count == N) + m_sum -= m_buffer[m_index]; + // new entry into buffer + m_buffer[m_index] = data; + // move index to next position with wrap around + if (++m_index >= N) + m_index = 0; + // keep count moving until buffer is full + if (m_count < N) + ++m_count; + // return average of current items + return m_sum / m_count; + } + inline void reset() + { + m_count = 0; + m_sum = 0; + m_index = 0; + } + inline int count() { return m_count; } + inline int sum() { return m_sum; } + T oldest() + { + // undefined if nothing in here, return zero + if (m_count == 0) + return 0; + // if it is not full, oldest is at index 0 + // if full, it is right where the next one goes + if (m_count < N) + return m_buffer[0]; + else + return m_buffer[m_index]; + } + T newest() + { + // undefined if nothing in here, return zero + if (m_count == 0) + return 0; + // newest is index - 1, with wrap + int index = m_index; + if (--index < 0) + index = m_count-1; + return m_buffer[index]; + } + T average() + { + if (m_count == 0) + return 0; + return m_sum / m_count; + } +}; + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +// DecayingAverage Class Declaration +// + +template +class DecayingAverage +{ +private: + T m_last; + bool m_lastvalid; + +public: + inline DecayingAverage() { reset(); } + T filter(T data, int fingers) + { + TT result = data; + TT last = m_last; + if (m_lastvalid) + result = (result * N1) / D + (last * N2) / D; + m_lastvalid = true; + m_last = (T)result; + return m_last; + } + inline void reset() + { + m_lastvalid = false; + } +}; + +template +class UndecayAverage +{ +private: + T m_last; + bool m_lastvalid; + +public: + inline UndecayAverage() { reset(); } + T filter(T data) + { + TT result = data; + TT last = m_last; + if (m_lastvalid) + result = (result * D) / N1 - (last * N2) / N1; + m_lastvalid = true; + m_last = (T)data; + return (T)result; + } + inline void reset() + { + m_lastvalid = false; + } +}; + + +#endif diff --git a/VoodooPS2Trackpad/VoodooPS2TouchPadBase.cpp b/VoodooPS2Trackpad/VoodooPS2TouchPadBase.cpp new file mode 100644 index 0000000..f0fd832 --- /dev/null +++ b/VoodooPS2Trackpad/VoodooPS2TouchPadBase.cpp @@ -0,0 +1,1017 @@ +// +// Created by Brandon Pedersen on 5/1/13. +// + +#include +#include +#include +#include +#include "VoodooPS2Controller.h" +#include "VoodooPS2TouchPadBase.h" + +// ============================================================================= +// VoodooPS2TouchPadBase Class Implementation +// + +OSDefineMetaClassAndAbstractStructors(VoodooPS2TouchPadBase, IOHIPointing); + +UInt32 VoodooPS2TouchPadBase::deviceType() +{ return NX_EVS_DEVICE_TYPE_MOUSE; }; + +UInt32 VoodooPS2TouchPadBase::interfaceID() +{ return NX_EVS_DEVICE_INTERFACE_BUS_ACE; }; + +IOItemCount VoodooPS2TouchPadBase::buttonCount() { return _buttonCount; }; +IOFixed VoodooPS2TouchPadBase::resolution() { return _resolution << 16; }; + +#define abs(x) ((x) < 0 ? -(x) : (x)) + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +bool VoodooPS2TouchPadBase::init(OSDictionary * dict) +{ + // + // Initialize this object's minimal state. This is invoked right after this + // object is instantiated. + // + + if (!super::init(dict)) + return false; + + // find config specific to Platform Profile + OSDictionary* list = OSDynamicCast(OSDictionary, dict->getObject(kPlatformProfile)); + OSDictionary* config = ApplePS2Controller::makeConfigurationNode(list); + if (config) + { + // if DisableDevice is Yes, then do not load at all... + OSBoolean* disable = OSDynamicCast(OSBoolean, config->getObject(kDisableDevice)); + if (disable && disable->isTrue()) + { + config->release(); + return false; + } +#ifdef DEBUG + // save configuration for later/diagnostics... + setProperty(kMergedConfiguration, config); +#endif + } + + // initialize state... + _device = NULL; + _interruptHandlerInstalled = false; + _powerControlHandlerInstalled = false; + _messageHandlerInstalled = false; + _packetByteCount = 0; + _lastdata = 0; + _cmdGate = 0; + + // set defaults for configuration items + + /*z_finger=45; + divisorx=divisory=1; + ledge=1700; + redge=5200; + tedge=4200; + bedge=1700; + vscrolldivisor=30; + hscrolldivisor=30; + cscrolldivisor=0; + ctrigger=0; + centerx=3000; + centery=3000; + maxtaptime=130000000; + maxdragtime=230000000; + hsticky=0; + vsticky=0; + wsticky=0; + tapstable=1; + wlimit=9; + wvdivisor=30; + whdivisor=30; + clicking=true; + rtap=true; + dragging=true; + threefingervertswipe=true; + threefingerhorizswipe=true; + draglock=false; + draglocktemp=0; + hscroll=vscroll=true; + scroll=true; + outzone_wt = palm = palm_wt = false; + zlimit = 100; + noled = false; + maxaftertyping = 500000000; + mousemultiplierx = 20; + mousemultipliery = 20; + mousescrollmultiplierx = 20; + mousescrollmultipliery = 20; + mousemiddlescroll = true; + wakedelay = 1000; + skippassthru = false; + tapthreshx = tapthreshy = 50; + dblthreshx = dblthreshy = 100; + zonel = 1700; zoner = 5200; + zonet = 99999; zoneb = 0; + diszl = 0; diszr = 1700; + diszt = 99999; diszb = 4200; + diszctrl = 0; + _resolution = 2300; + _scrollresolution = 2300; + swipedx = swipedy = 800; + rczl = 3800; rczt = 2000; + rczr = 99999; rczb = 0; + _buttonCount = 2; + swapdoubletriple = false; + draglocktempmask = 0x0100010; // default is Command key + clickpadclicktime = 300000000; // 300ms default + clickpadtrackboth = true; + + bogusdxthresh = 400; + bogusdythresh = 350; + + scrolldxthresh = 10; + scrolldythresh = 10; + + immediateclick = true; + + xupmm = yupmm = 50; // 50 is just arbitrary, but same + + _extendedwmode=false; + + // intialize state + + lastx=0; + lasty=0; + last_fingers=0; + xrest=0; + yrest=0; + lastbuttons=0; + + // intialize state for secondary packets/extendedwmode + xrest2=0; + yrest2=0; + clickedprimary=false; + lastx2=0; + lasty2=0; + tracksecondary=false; + + // state for middle button + _buttonTimer = 0; + _mbuttonstate = STATE_NOBUTTONS; + _pendingbuttons = 0; + _buttontime = 0; + _maxmiddleclicktime = 100000000; + _fakemiddlebutton = true; + + ignoredeltas=0; + ignoredeltasstart=0; + scrollrest=0; + touchtime=untouchtime=0; + wastriple=wasdouble=false; + keytime = 0; + ignoreall = false; + passbuttons = 0; + passthru = false; + ledpresent = false; + clickpadtype = 0; + _clickbuttons = 0; + _reportsv = false; + mousecount = 0; + usb_mouse_stops_trackpad = true; + _modifierdown = 0; + scrollzoommask = 0; + + inSwipeLeft=inSwipeRight=inSwipeDown=inSwipeUp=0; + xmoved=ymoved=0; + + momentumscroll = true; + scrollTimer = 0; + momentumscrolltimer = 10000000; + momentumscrollthreshy = 7; + momentumscrollmultiplier = 98; + momentumscrolldivisor = 100; + momentumscrollsamplesmin = 3; + momentumscrollcurrent = 0; + + dragexitdelay = 100000000; + dragTimer = 0; + + touchmode=MODE_NOTOUCH;*/ + + IOLog("VoodooPS2TouchPad Base Driver loaded...\n"); + + setProperty("Revision", 24, 32); + + // + // Load settings specific to Platform Profile + // + + setParamPropertiesGated(config); + OSSafeReleaseNULL(config); + + return true; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +bool VoodooPS2TouchPadBase::start( IOService * provider ) +{ + // + // The driver has been instructed to start. This is called after a + // successful probe and match. + // + + if (!super::start(provider)) + return false; + + // + // Maintain a pointer to and retain the provider object. + // + + _device = (ApplePS2MouseDevice *) provider; + _device->retain(); + + // + // Advertise the current state of the tapping feature. + // + // Must add this property to let our superclass know that it should handle + // trackpad acceleration settings from user space. Without this, tracking + // speed adjustments from the mouse prefs panel have no effect. + // + + setProperty(kIOHIDPointerAccelerationTypeKey, kIOHIDTrackpadAccelerationType); + setProperty(kIOHIDScrollAccelerationTypeKey, kIOHIDTrackpadScrollAccelerationKey); + setProperty(kIOHIDScrollResolutionKey, _scrollresolution << 16, 32); + setProperty("HIDScrollResolutionX", _scrollresolution << 16, 32); + setProperty("HIDScrollResolutionY", _scrollresolution << 16, 32); + + // + // Setup workloop with command gate for thread synchronization... + // + IOWorkLoop* pWorkLoop = getWorkLoop(); + _cmdGate = IOCommandGate::commandGate(this); + if (!pWorkLoop || !_cmdGate) + { + _device->release(); + return false; + } + + // + // Setup button timer event source + // + if (_buttonCount >= 3) + { + _buttonTimer = IOTimerEventSource::timerEventSource(this, OSMemberFunctionCast(IOTimerEventSource::Action, this, &VoodooPS2TouchPadBase::onButtonTimer)); + if (!_buttonTimer) + { + _device->release(); + return false; + } + pWorkLoop->addEventSource(_buttonTimer); + } + + // + // Setup dragTimer event source + // + if (dragexitdelay) + { + dragTimer = IOTimerEventSource::timerEventSource(this, OSMemberFunctionCast(IOTimerEventSource::Action, this, &VoodooPS2TouchPadBase::onDragTimer)); + if (dragTimer) + pWorkLoop->addEventSource(dragTimer); + } + + pWorkLoop->addEventSource(_cmdGate); + + // + // Setup scrolltimer event source + // + scrollTimer = IOTimerEventSource::timerEventSource(this, OSMemberFunctionCast(IOTimerEventSource::Action, this, &VoodooPS2TouchPadBase::onScrollTimer)); + if (scrollTimer) + pWorkLoop->addEventSource(scrollTimer); + + // + // Lock the controller during initialization + // + + _device->lock(); + + + // + // Perform any implementation specific device initialization + // + if (!deviceSpecificInit()) { + _device->unlock(); + _device->release(); + // TODO: any other cleanup? + return false; + } + + // + // Install our driver's interrupt handler, for asynchronous data delivery. + // + + _device->installInterruptAction(this, + OSMemberFunctionCast(PS2InterruptAction,this,&VoodooPS2TouchPadBase::interruptOccurred), + OSMemberFunctionCast(PS2PacketAction, this, &VoodooPS2TouchPadBase::packetReady)); + _interruptHandlerInstalled = true; + + // now safe to allow other threads + _device->unlock(); + + // + // Install our power control handler. + // + + _device->installPowerControlAction( this, + OSMemberFunctionCast(PS2PowerControlAction, this, &VoodooPS2TouchPadBase::setDevicePowerState) ); + _powerControlHandlerInstalled = true; + + // + // Install message hook for keyboard to trackpad communication + // + + _device->installMessageAction( this, + OSMemberFunctionCast(PS2MessageAction, this, &VoodooPS2TouchPadBase::receiveMessage)); + _messageHandlerInstalled = true; + + return true; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +void VoodooPS2TouchPadBase::stop( IOService * provider ) +{ + DEBUG_LOG("%s: stop called\n", getName()); + + // + // The driver has been instructed to stop. Note that we must break all + // connections to other service objects now (ie. no registered actions, + // no pointers and retains to objects, etc), if any. + // + + assert(_device == provider); + + // free up timer for scroll momentum + IOWorkLoop* pWorkLoop = getWorkLoop(); + if (pWorkLoop) + { + if (scrollTimer) + { + pWorkLoop->removeEventSource(scrollTimer); + scrollTimer->release(); + scrollTimer = 0; + } + if (_buttonTimer) + { + pWorkLoop->removeEventSource(_buttonTimer); + _buttonTimer->release(); + _buttonTimer = 0; + } + if (_cmdGate) + { + pWorkLoop->removeEventSource(_cmdGate); + _cmdGate->release(); + _cmdGate = 0; + } + } + + // + // Uninstall the interrupt handler. + // + + if (_interruptHandlerInstalled) + { + _device->uninstallInterruptAction(); + _interruptHandlerInstalled = false; + } + + // + // Uninstall the power control handler. + // + + if (_powerControlHandlerInstalled) + { + _device->uninstallPowerControlAction(); + _powerControlHandlerInstalled = false; + } + + // + // Uinstall message handler. + // + if (_messageHandlerInstalled) + { + _device->uninstallMessageAction(); + _messageHandlerInstalled = false; + } + + // + // Release the pointer to the provider object. + // + + OSSafeReleaseNULL(_device); + + super::stop(provider); +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +void VoodooPS2TouchPadBase::onScrollTimer(void) +{ + // + // This will be invoked by our workloop timer event source to implement + // momentum scroll. + // + + if (!momentumscrollcurrent) + return; + + uint64_t now_abs; + clock_get_uptime(&now_abs); + + int64_t dy64 = momentumscrollcurrent / (int64_t)momentumscrollinterval + momentumscrollrest2; + int dy = (int)dy64; + if (abs(dy) > momentumscrollthreshy) + { + // dispatch the scroll event + dispatchScrollWheelEventX(wvdivisor ? dy / wvdivisor : 0, 0, 0, now_abs); + momentumscrollrest2 = wvdivisor ? dy % wvdivisor : 0; + + // adjust momentumscrollcurrent + momentumscrollcurrent = momentumscrollcurrent * momentumscrollmultiplier + momentumscrollrest1; + momentumscrollrest1 = momentumscrollcurrent % momentumscrolldivisor; + momentumscrollcurrent /= momentumscrolldivisor; + + // start another timer + setTimerTimeout(scrollTimer, momentumscrolltimer); + } + else + { + // no more scrolling... + momentumscrollcurrent = 0; + } +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +void VoodooPS2TouchPadBase::onButtonTimer(void) +{ + uint64_t now_abs; + clock_get_uptime(&now_abs); + + middleButton(lastbuttons, now_abs, fromTimer); +} + +UInt32 VoodooPS2TouchPadBase::middleButton(UInt32 buttons, uint64_t now_abs, MBComingFrom from) +{ + if (!_fakemiddlebutton || _buttonCount <= 2 || (ignoreall && fromTrackpad == from)) + return buttons; + + // cancel timer if we see input before timeout has fired, but after expired + bool timeout = false; + uint64_t now_ns; + absolutetime_to_nanoseconds(now_abs, &now_ns); + if (fromTimer == from || fromCancel == from || now_ns - _buttontime > _maxmiddleclicktime) + timeout = true; + + // + // A state machine to simulate middle buttons with two buttons pressed + // together. + // + switch (_mbuttonstate) + { + // no buttons down, waiting for something to happen + case STATE_NOBUTTONS: + if (fromCancel != from) + { + if (buttons & 0x4) + _mbuttonstate = STATE_NOOP; + else if (0x3 == buttons) + _mbuttonstate = STATE_MIDDLE; + else if (0x0 != buttons) + { + // only single button, so delay this for a bit + _pendingbuttons = buttons; + _buttontime = now_ns; + setTimerTimeout(_buttonTimer, _maxmiddleclicktime); + _mbuttonstate = STATE_WAIT4TWO; + } + } + break; + + // waiting for second button to come down or timeout + case STATE_WAIT4TWO: + if (!timeout && 0x3 == buttons) + { + _pendingbuttons = 0; + cancelTimer(_buttonTimer); + _mbuttonstate = STATE_MIDDLE; + } + else if (timeout || buttons != _pendingbuttons) + { + if (fromTimer == from || !(buttons & _pendingbuttons)) + dispatchRelativePointerEventX(0, 0, buttons|_pendingbuttons, now_abs); + _pendingbuttons = 0; + cancelTimer(_buttonTimer); + if (0x0 == buttons) + _mbuttonstate = STATE_NOBUTTONS; + else + _mbuttonstate = STATE_NOOP; + } + break; + + // both buttons down and delivering middle button + case STATE_MIDDLE: + if (0x0 == buttons) + _mbuttonstate = STATE_NOBUTTONS; + else if (0x3 != (buttons & 0x3)) + { + // only single button, so delay to see if we get to none + _pendingbuttons = buttons; + _buttontime = now_ns; + setTimerTimeout(_buttonTimer, _maxmiddleclicktime); + _mbuttonstate = STATE_WAIT4NONE; + } + break; + + // was middle button, but one button now up, waiting for second to go up + case STATE_WAIT4NONE: + if (!timeout && 0x0 == buttons) + { + _pendingbuttons = 0; + cancelTimer(_buttonTimer); + _mbuttonstate = STATE_NOBUTTONS; + } + else if (timeout || buttons != _pendingbuttons) + { + if (fromTimer == from) + dispatchRelativePointerEventX(0, 0, buttons|_pendingbuttons, now_abs); + _pendingbuttons = 0; + cancelTimer(_buttonTimer); + if (0x0 == buttons) + _mbuttonstate = STATE_NOBUTTONS; + else + _mbuttonstate = STATE_NOOP; + } + break; + + case STATE_NOOP: + if (0x0 == buttons) + _mbuttonstate = STATE_NOBUTTONS; + break; + } + + // modify buttons after new state set + switch (_mbuttonstate) + { + case STATE_MIDDLE: + buttons = 0x4; + break; + + case STATE_WAIT4NONE: + case STATE_WAIT4TWO: + buttons &= ~0x3; + break; + + case STATE_NOBUTTONS: + case STATE_NOOP: + break; + } + + // return modified buttons + return buttons; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +void VoodooPS2TouchPadBase::onDragTimer(void) +{ + if (MODE_DRAGNOTOUCH==touchmode) + { + touchmode=MODE_NOTOUCH; + + uint64_t now_abs; + clock_get_uptime(&now_abs); + UInt32 buttons = middleButton(lastbuttons & ~0x01, now_abs, fromPassthru); + DEBUG_LOG("ps2: onDragTimer, button = %d\n", buttons); + dispatchRelativePointerEventX(0, 0, buttons, now_abs); + } + else + { + //REVIEW: for debugging... + IOLog("rehab: onDragTimer called with unexpected mode = %d\n", touchmode); + } + //TODO: cancel dragnotouch mode, revert to notouch + //TODO: send lbutton up without modifying other buttons + //TODO: find other places the timer should be cancelled. +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +void VoodooPS2TouchPadBase::initTouchPad() +{ + // + // Clear packet buffer pointer to avoid issues caused by + // stale packet fragments. + // + + _packetByteCount = 0; + _ringBuffer.reset(); + + // clear passbuttons, just in case buttons were down when system + // went to sleep (now just assume they are up) + passbuttons = 0; + _clickbuttons = 0; + tracksecondary=false; + + // clear state of control key cache + _modifierdown = 0; + + // initialize the touchpad + deviceSpecificInit(); +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +void VoodooPS2TouchPadBase::setParamPropertiesGated(OSDictionary * config) +{ + if (NULL == config) + return; + + const struct {const char *name; int *var;} int32vars[]={ + {"FingerZ", &z_finger}, + {"DivisorX", &divisorx}, + {"DivisorY", &divisory}, + {"EdgeRight", &redge}, + {"EdgeLeft", &ledge}, + {"EdgeTop", &tedge}, + {"EdgeBottom", &bedge}, + {"VerticalScrollDivisor", &vscrolldivisor}, + {"HorizontalScrollDivisor", &hscrolldivisor}, + {"CircularScrollDivisor", &cscrolldivisor}, + {"CenterX", ¢erx}, + {"CenterY", ¢ery}, + {"CircularScrollTrigger", &ctrigger}, + {"MultiFingerWLimit", &wlimit}, + {"MultiFingerVerticalDivisor", &wvdivisor}, + {"MultiFingerHorizontalDivisor", &whdivisor}, + {"ZLimit", &zlimit}, + {"MouseMultiplierX", &mousemultiplierx}, + {"MouseMultiplierY", &mousemultipliery}, + {"MouseScrollMultiplierX", &mousescrollmultiplierx}, + {"MouseScrollMultiplierY", &mousescrollmultipliery}, + {"WakeDelay", &wakedelay}, + {"TapThresholdX", &tapthreshx}, + {"TapThresholdY", &tapthreshy}, + {"DoubleTapThresholdX", &dblthreshx}, + {"DoubleTapThresholdY", &dblthreshy}, + {"ZoneLeft", &zonel}, + {"ZoneRight", &zoner}, + {"ZoneTop", &zonet}, + {"ZoneBottom", &zoneb}, + {"DisableZoneLeft", &diszl}, + {"DisableZoneRight", &diszr}, + {"DisableZoneTop", &diszt}, + {"DisableZoneBottom", &diszb}, + {"DisableZoneControl", &diszctrl}, + {"Resolution", &_resolution}, + {"ScrollResolution", &_scrollresolution}, + {"SwipeDeltaX", &swipedx}, + {"SwipeDeltaY", &swipedy}, + {"MouseCount", &mousecount}, + {"RightClickZoneLeft", &rczl}, + {"RightClickZoneRight", &rczr}, + {"RightClickZoneTop", &rczt}, + {"RightClickZoneBottom", &rczb}, + {"HIDScrollZoomModifierMask", &scrollzoommask}, + {"ButtonCount", &_buttonCount}, + {"DragLockTempMask", &draglocktempmask}, + {"MomentumScrollThreshY", &momentumscrollthreshy}, + {"MomentumScrollMultiplier", &momentumscrollmultiplier}, + {"MomentumScrollDivisor", &momentumscrolldivisor}, + {"MomentumScrollSamplesMin", &momentumscrollsamplesmin}, + {"FingerChangeIgnoreDeltas", &ignoredeltasstart}, + {"BogusDeltaThreshX", &bogusdxthresh}, + {"BogusDeltaThreshY", &bogusdythresh}, + {"UnitsPerMMX", &xupmm}, + {"UnitsPerMMY", &yupmm}, + {"ScrollDeltaThreshX", &scrolldxthresh}, + {"ScrollDeltaThreshY", &scrolldythresh}, + {"TrackpadThreeFingerVertSwipeGesture", &threefingervertswipe}, + {"TrackpadThreeFingerHorizSwipeGesture", &threefingerhorizswipe}, + }; + const struct {const char *name; int *var;} boolvars[]={ + {"StickyHorizontalScrolling", &hsticky}, + {"StickyVerticalScrolling", &vsticky}, + {"StickyMultiFingerScrolling", &wsticky}, + {"StabilizeTapping", &tapstable}, + {"DisableLEDUpdate", &noled}, + {"SmoothInput", &smoothinput}, + {"UnsmoothInput", &unsmoothinput}, + {"SkipPassThrough", &skippassthru}, + {"SwapDoubleTriple", &swapdoubletriple}, + {"ClickPadTrackBoth", &clickpadtrackboth}, + {"ImmediateClick", &immediateclick}, + {"MouseMiddleScroll", &mousemiddlescroll}, + {"FakeMiddleButton", &_fakemiddlebutton}, + }; + const struct {const char* name; bool* var;} lowbitvars[]={ + {"Clicking", &clicking}, + {"Dragging", &dragging}, + {"TrackpadRightClick", &rtap}, + {"DragLock", &draglock}, + {"TrackpadHorizScroll", &hscroll}, + {"TrackpadVertScroll", &vscroll}, + {"TrackpadScroll", &scroll}, + {"OutsidezoneNoAction When Typing", &outzone_wt}, + {"PalmNoAction Permanent", &palm}, + {"PalmNoAction When Typing", &palm_wt}, + {"USBMouseStopsTrackpad", &usb_mouse_stops_trackpad}, + {"TrackpadMomentumScroll", &momentumscroll}, + }; + const struct {const char* name; uint64_t* var; } int64vars[]={ + {"MaxDragTime", &maxdragtime}, + {"MaxTapTime", &maxtaptime}, + {"HIDClickTime", &maxdbltaptime}, + {"QuietTimeAfterTyping", &maxaftertyping}, + {"MomentumScrollTimer", &momentumscrolltimer}, + {"ClickPadClickTime", &clickpadclicktime}, + {"MiddleClickTime", &_maxmiddleclicktime}, + {"DragExitDelayTime", &dragexitdelay}, + }; + + int oldmousecount = mousecount; + bool old_usb_mouse_stops_trackpad = usb_mouse_stops_trackpad; + + OSBoolean *bl; + OSNumber *num; + // 64-bit config items + for (int i = 0; i < countof(int64vars); i++) { + if ((num=OSDynamicCast(OSNumber, config->getObject(int64vars[i].name)))) + { + *int64vars[i].var = num->unsigned64BitValue(); + ////DEBUG_LOG("%s::setProperty64(%s, %llu)\n", getName(), int64vars[i].name, *int64vars[i].var); + setProperty(int64vars[i].name, *int64vars[i].var, 64); + } + } + // boolean config items + for (int i = 0; i < countof(boolvars); i++) { + if ((bl=OSDynamicCast (OSBoolean,config->getObject (boolvars[i].name)))) + { + *boolvars[i].var = bl->isTrue(); + ////DEBUG_LOG("%s::setPropertyBool(%s, %d)\n", getName(), boolvars[i].name, *boolvars[i].var); + setProperty(boolvars[i].name, *boolvars[i].var ? kOSBooleanTrue : kOSBooleanFalse); + } + } + // 32-bit config items + for (int i = 0; i < countof(int32vars);i++) { + if ((num=OSDynamicCast (OSNumber,config->getObject (int32vars[i].name)))) + { + *int32vars[i].var = num->unsigned32BitValue(); + ////DEBUG_LOG("%s::setProperty32(%s, %d)\n", getName(), int32vars[i].name, *int32vars[i].var); + setProperty(int32vars[i].name, *int32vars[i].var, 32); + } + } + // lowbit config items + for (int i = 0; i < countof(lowbitvars); i++) { + if ((num=OSDynamicCast (OSNumber,config->getObject(lowbitvars[i].name)))) + { + *lowbitvars[i].var = (num->unsigned32BitValue()&0x1)?true:false; + ////DEBUG_LOG("%s::setPropertyLowBit(%s, %d)\n", getName(), lowbitvars[i].name, *lowbitvars[i].var); + setProperty(lowbitvars[i].name, *lowbitvars[i].var ? 1 : 0, 32); + } + } + + // special case for MaxDragTime (which is really max time for a double-click) + // we can let it go no more than 230ms because otherwise taps on + // the menu bar take too long if drag mode is enabled. The code in that case + // has to "hold button 1 down" for the duration of maxdragtime because if + // it didn't then dragging on the caption of a window will not work + // (some other apps too) because these apps will see a double tap+hold as + // a single click, then double click and they don't go into drag mode when + // initiated with a double click. + // + // same thing going on with the forward/back buttons in Finder, except the + // timeout OS X is using is different (shorter) + // + // this all happens during MODE_PREDRAG + // + // summary: + // if the code releases button 1 after a tap, then dragging windows + // breaks + // if the maxdragtime is too large (200ms is small enough, 500ms is too large) + // then clicking on menus breaks because the system sees it as a long + // press and hold + // + // fyi: + // also tried to allow release of button 1 during MODE_PREDRAG, and then when + // attempting to initiate the drag (in the case the second touch comes soon + // enough), modifying the time such that it is not seen as a double tap. + // unfortunately, that destroys double tap as well, probably because the + // system is confused seeing input "out of order" + + //if (maxdragtime > 230000000) + // maxdragtime = 230000000; + + // DivisorX and DivisorY cannot be zero, but don't crash if they are... + if (!divisorx) + divisorx = 1; + if (!divisory) + divisory = 1; + + // bogusdeltathreshx/y = 0 is MAX_INT + if (!bogusdxthresh) + bogusdxthresh = 0x7FFFFFFF; + if (!bogusdythresh) + bogusdythresh = 0x7FFFFFFF; + +//REVIEW: this should be done maybe only when necessary... + touchmode=MODE_NOTOUCH; + + // check for special terminating sequence from PS2Daemon + if (-1 == mousecount) + { + DEBUG_LOG("Shutdown touchpad, mousecount=%d\n", mousecount); + touchpadShutdown(); + mousecount = oldmousecount; + } + + // disable trackpad when USB mouse is plugged in + // check for mouse count changing... + if ((oldmousecount != 0) != (mousecount != 0) || old_usb_mouse_stops_trackpad != usb_mouse_stops_trackpad) + { + // either last mouse removed or first mouse added + ignoreall = (mousecount != 0) && usb_mouse_stops_trackpad; + touchpadToggled(); + } +} + +IOReturn VoodooPS2TouchPadBase::setParamProperties(OSDictionary* dict) +{ + ////IOReturn result = super::IOHIDevice::setParamProperties(dict); + if (_cmdGate) + { + // syncronize through workloop... + ////_cmdGate->runAction(OSMemberFunctionCast(IOCommandGate::Action, this, &VooodooPS2TouchPadBase::setParamPropertiesGated), dict); + setParamPropertiesGated(dict); + } + + return super::setParamProperties(dict); + ////return result; +} + +IOReturn VoodooPS2TouchPadBase::setProperties(OSObject *props) +{ + OSDictionary *dict = OSDynamicCast(OSDictionary, props); + if (dict && _cmdGate) + { + // syncronize through workloop... + _cmdGate->runAction(OSMemberFunctionCast(IOCommandGate::Action, this, &VoodooPS2TouchPadBase::setParamPropertiesGated), dict); + } + + return super::setProperties(props); +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +void VoodooPS2TouchPadBase::setDevicePowerState( UInt32 whatToDo ) +{ + switch ( whatToDo ) + { + case kPS2C_DisableDevice: + // + // Disable touchpad (synchronous). + // + + setTouchPadEnable( false ); + break; + + case kPS2C_EnableDevice: + // + // Must not issue any commands before the device has + // completed its power-on self-test and calibration. + // + + IOSleep(wakedelay); + + // Reset and enable the touchpad. + initTouchPad(); + break; + } +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +void VoodooPS2TouchPadBase::receiveMessage(int message, void* data) +{ + // + // Here is where we receive messages from the keyboard driver + // + // This allows for the keyboard driver to enable/disable the trackpad + // when a certain keycode is pressed. + // + // It also allows the trackpad driver to learn the last time a key + // has been pressed, so it can implement various "ignore trackpad + // input while typing" options. + // + switch (message) + { + case kPS2M_getDisableTouchpad: + { + bool* pResult = (bool*)data; + *pResult = !ignoreall; + break; + } + + case kPS2M_setDisableTouchpad: + { + bool enable = *((bool*)data); + // ignoreall is true when trackpad has been disabled + if (enable == ignoreall) + { + // save state, and update LED + ignoreall = !enable; + touchpadToggled(); + } + break; + } + + case kPS2M_notifyKeyPressed: + { + // just remember last time key pressed... this can be used in + // interrupt handler to detect unintended input while typing + PS2KeyInfo* pInfo = (PS2KeyInfo*)data; + static const int masks[] = + { + 0x10, // 0x36 + 0x100000, // 0x37 + 0, // 0x38 + 0, // 0x39 + 0x080000, // 0x3a + 0x040000, // 0x3b + 0, // 0x3c + 0x08, // 0x3d + 0x04, // 0x3e + 0x200000, // 0x3f + }; +#ifdef SIMULATE_PASSTHRU + static int buttons = 0; + int button; + switch (pInfo->adbKeyCode) + { + // make right Alt,Menu,Ctrl into three button passthru + case 0x36: + button = 0x1; + goto dispatch_it; + case 0x3f: + button = 0x4; + goto dispatch_it; + case 0x3e: + button = 0x2; + // fall through... + dispatch_it: + if (pInfo->goingDown) + buttons |= button; + else + buttons &= ~button; + UInt8 packet[6]; + packet[0] = 0x84 | trackbuttons; + packet[1] = 0x08 | buttons; + packet[2] = 0; + packet[3] = 0xC4 | trackbuttons; + packet[4] = 0; + packet[5] = 0; + dispatchEventsWithPacket(packet, 6); + pInfo->eatKey = true; + } +#endif + switch (pInfo->adbKeyCode) + { + // don't store key time for modifier keys going down + // track modifiers for scrollzoom feature... + // (note: it turns out we didn't need to do this, but leaving this code in for now in case it is useful) + case 0x38: // left shift + case 0x3c: // right shift + case 0x3b: // left control + case 0x3e: // right control + case 0x3a: // left windows (option) + case 0x3d: // right windows + case 0x37: // left alt (command) + case 0x36: // right alt + case 0x3f: // osx fn (function) + if (pInfo->goingDown) + { + _modifierdown |= masks[pInfo->adbKeyCode-0x36]; + break; + } + _modifierdown &= ~masks[pInfo->adbKeyCode-0x36]; + keytime = pInfo->time; + break; + + default: + momentumscrollcurrent = 0; // keys cancel momentum scroll + keytime = pInfo->time; + } + break; + } + } +} diff --git a/VoodooPS2Trackpad/VoodooPS2TouchPadBase.h b/VoodooPS2Trackpad/VoodooPS2TouchPadBase.h new file mode 100644 index 0000000..cf15157 --- /dev/null +++ b/VoodooPS2Trackpad/VoodooPS2TouchPadBase.h @@ -0,0 +1,265 @@ +// +// Created by Brandon Pedersen on 5/1/13. +// + +#ifndef __VoodooPS2TouchPadBase_H_ +#define __VoodooPS2TouchPadBase_H_ + +#include "ApplePS2MouseDevice.h" +#include +#include +#include +#include "Decay.h" + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +// VoodooPS2TouchPadBase Class Declaration +// + +#define kPacketLength 6 + +class EXPORT VoodooPS2TouchPadBase : public IOHIPointing +{ + typedef IOHIPointing super; + OSDeclareAbstractStructors(VoodooPS2TouchPadBase); + +protected: + ApplePS2MouseDevice * _device; + bool _interruptHandlerInstalled; + bool _powerControlHandlerInstalled; + bool _messageHandlerInstalled; + RingBuffer _ringBuffer; + UInt32 _packetByteCount; + UInt8 _lastdata; + UInt16 _touchPadVersion; + + IOCommandGate* _cmdGate; + int z_finger; + int divisorx, divisory; + int ledge; + int redge; + int tedge; + int bedge; + int vscrolldivisor, hscrolldivisor, cscrolldivisor; + int ctrigger; + int centerx; + int centery; + uint64_t maxtaptime; + uint64_t maxdragtime; + uint64_t maxdbltaptime; + int hsticky,vsticky, wsticky, tapstable; + int wlimit, wvdivisor, whdivisor; + bool clicking; + bool dragging; + int threefingervertswipe; + int threefingerhorizswipe; + bool draglock; + int draglocktemp; + bool hscroll, vscroll, scroll; + bool rtap; + bool outzone_wt, palm, palm_wt; + int zlimit; + int noled; + uint64_t maxaftertyping; + int mousemultiplierx, mousemultipliery; + int mousescrollmultiplierx, mousescrollmultipliery; + int mousemiddlescroll; + int wakedelay; + int smoothinput; + int unsmoothinput; + int skippassthru; + int tapthreshx, tapthreshy; + int dblthreshx, dblthreshy; + int zonel, zoner, zonet, zoneb; + int diszl, diszr, diszt, diszb; + int diszctrl; // 0=automatic (ledpresent), 1=enable always, -1=disable always + int _resolution, _scrollresolution; + int swipedx, swipedy; + int _buttonCount; + int swapdoubletriple; + int draglocktempmask; + uint64_t clickpadclicktime; + int clickpadtrackboth; + int ignoredeltasstart; + int bogusdxthresh, bogusdythresh; + int scrolldxthresh, scrolldythresh; + int immediateclick; + + // three finger and four finger state + uint8_t inSwipeLeft, inSwipeRight; + uint8_t inSwipeUp, inSwipeDown; + uint8_t inSwipe4Left, inSwipe4Right; + uint8_t inSwipe4Up, inSwipe4Down; + int xmoved, ymoved; + + int rczl, rczr, rczb, rczt; // rightclick zone for 1-button ClickPads + + // state related to secondary packets/extendedwmode + int lastx2, lasty2; + bool tracksecondary; + int xrest2, yrest2; + bool clickedprimary; + bool _extendedwmode; + + // normal state + int lastx, lasty, last_fingers, b4last; + UInt32 lastbuttons; + int ignoredeltas; + int xrest, yrest, scrollrest; + int touchx, touchy; + uint64_t touchtime; + uint64_t untouchtime; + bool wasdouble,wastriple; + uint64_t keytime; + bool ignoreall; + UInt32 passbuttons; +#ifdef SIMULATE_PASSTHRU + UInt32 trackbuttons; +#endif + bool passthru; + bool ledpresent; + bool _reportsv; + int clickpadtype; //0=not, 1=1button, 2=2button, 3=reserved + UInt32 _clickbuttons; //clickbuttons to merge into buttons + int mousecount; + bool usb_mouse_stops_trackpad; + + int _modifierdown; // state of left+right control keys + int scrollzoommask; + + // for scaling x/y values + int xupmm, yupmm; + + // for middle button simulation + enum mbuttonstate + { + STATE_NOBUTTONS, + STATE_MIDDLE, + STATE_WAIT4TWO, + STATE_WAIT4NONE, + STATE_NOOP, + } _mbuttonstate; + + UInt32 _pendingbuttons; + uint64_t _buttontime; + IOTimerEventSource* _buttonTimer; + uint64_t _maxmiddleclicktime; + int _fakemiddlebutton; + + // momentum scroll state + bool momentumscroll; + SimpleAverage dy_history; + SimpleAverage time_history; + IOTimerEventSource* scrollTimer; + uint64_t momentumscrolltimer; + int momentumscrollthreshy; + uint64_t momentumscrollinterval; + int momentumscrollsum; + int64_t momentumscrollcurrent; + int64_t momentumscrollrest1; + int momentumscrollmultiplier; + int momentumscrolldivisor; + int momentumscrollrest2; + int momentumscrollsamplesmin; + + // timer for drag delay + uint64_t dragexitdelay; + IOTimerEventSource* dragTimer; + + SimpleAverage x_avg; + SimpleAverage y_avg; + //DecayingAverage x_avg; + //DecayingAverage y_avg; + UndecayAverage x_undo; + UndecayAverage y_undo; + + SimpleAverage x2_avg; + SimpleAverage y2_avg; + //DecayingAverage x2_avg; + //DecayingAverage y2_avg; + UndecayAverage x2_undo; + UndecayAverage y2_undo; + + enum + { + // "no touch" modes... must be even (see isTouchMode) + MODE_NOTOUCH = 0, + MODE_PREDRAG = 2, + MODE_DRAGNOTOUCH = 4, + + // "touch" modes... must be odd (see isTouchMode) + MODE_MOVE = 1, + MODE_VSCROLL = 3, + MODE_HSCROLL = 5, + MODE_CSCROLL = 7, + MODE_MTOUCH = 9, + MODE_DRAG = 11, + MODE_DRAGLOCK = 13, + + // special modes for double click in LED area to enable/disable + // same "touch"/"no touch" odd/even rule (see isTouchMode) + MODE_WAIT1RELEASE = 101, // "touch" + MODE_WAIT2TAP = 102, // "no touch" + MODE_WAIT2RELEASE = 103, // "touch" + } touchmode; + + inline bool isTouchMode() { return touchmode & 1; } + + inline bool isInDisableZone(int x, int y) + { return x > diszl && x < diszr && y > diszb && y < diszt; } + + // Sony: coordinates captured from single touch event + // Don't know what is the exact value of x and y on edge of touchpad + // the best would be { return x > xmax/2 && y < ymax/4; } + + inline bool isInRightClickZone(int x, int y) + { return x > rczl && x < rczr && y > rczb && y < rczt; } + + virtual void setTouchPadEnable( bool enable ) = 0; + virtual PS2InterruptResult interruptOccurred(UInt8 data) = 0; + virtual void packetReady() = 0; + virtual void setDevicePowerState(UInt32 whatToDo); + + virtual void receiveMessage(int message, void* data); + + virtual void touchpadToggled() {}; + virtual void touchpadShutdown() {}; + virtual void initTouchPad(); + + inline bool isFingerTouch(int z) { return z>z_finger && zsetTimeout(*(AbsoluteTime*)&time); } + inline void cancelTimer(IOTimerEventSource* timer) + { timer->cancelTimeout(); } + +public: + virtual bool init( OSDictionary * properties ); + virtual VoodooPS2TouchPadBase * probe( IOService * provider, + SInt32 * score ) = 0; + virtual bool start( IOService * provider ); + virtual void stop( IOService * provider ); + + virtual UInt32 deviceType(); + virtual UInt32 interfaceID(); + + virtual IOReturn setParamProperties(OSDictionary * dict); + virtual IOReturn setProperties(OSObject *props); +}; + +#endif //__VoodooPS2TouchPadBase_H_ diff --git a/VoodooPS2Trackpad/VoodooPS2Trackpad-Info.plist b/VoodooPS2Trackpad/VoodooPS2Trackpad-Info.plist new file mode 100644 index 0000000..a1648c1 --- /dev/null +++ b/VoodooPS2Trackpad/VoodooPS2Trackpad-Info.plist @@ -0,0 +1,197 @@ + + + + + CFBundleExecutable + VoodooPS2Trackpad + CFBundleGetInfoString + ${MODULE_VERSION}, Copyright Apple Computer, Inc. 2002-2003, mackerintel 2008, RehabMan 2012-2013, Dr Hurt 2016 + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + Voodoo PS/2 Trackpad + CFBundlePackageType + KEXT + CFBundleShortVersionString + ${MODULE_VERSION} + CFBundleSignature + ???? + CFBundleVersion + ${MODULE_VERSION} + IOKitPersonalities + + ALPS TouchPad + + SupportsGestureScrolling + + TrackpadFourFingerGestures + + ApplePreferenceIdentifier + com.apple.AppleMultitouchTrackpad + MTHIDDevice + + MT Built-in + + ApplePreferenceCapability + + TrackpadEmbedded + + TrackpadThreeFingerDrag + + TrackpadSecondaryClickCorners + + CFBundleIdentifier + org.rehabman.voodoo.driver.PS2Trackpad + IOClass + ALPS + IOProbeScore + 6000 + IOProviderClass + ApplePS2MouseDevice + Platform Profile + + Default + + BogusDeltaThreshX + 0 + BogusDeltaThreshY + 0 + CenterX + 3000 + CenterY + 2100 + CircularScrollDivisor + 0 + CircularScrollTrigger + 0 + DisableDevice + + DisableLEDUpdating + + DivisorX + 5 + DivisorY + 5 + DoubleTapThresholdX + 100 + DoubleTapThresholdY + 100 + DragExitDelayTime + 1000000000 + DragLockTempMask + 1048592 + DoubleSmoothness + + EdgeBottom + 500 + EdgeLeft + 1700 + EdgeRight + 5200 + EdgeTop + 2940 + FingerChangeIgnoreDeltas + 0 + FingerZ + 30 + HorizontalScrollDivisor + 0 + ImmediateClick + + MaxDragTime + 180000000 + MaxTapTime + 130000000 + MomentumScrollDivisor + 100 + MomentumScrollMultiplier + 98 + MomentumScrollSamplesMin + 3 + MomentumScrollThreshY + 7 + MomentumScrollTimer + 10000000 + MultiFingerHorizontalDivisor + 5 + MultiFingerVerticalDivisor + 5 + QuietTimeAfterTyping + 500000000 + Resolution + 400 + ScrollDeltaThreshX + 0 + ScrollDeltaThreshY + 0 + ScrollResolution + 400 + SmoothInput + + StickyHorizontalScrolling + + StickyMultiFingerScrolling + + StickyVerticalScrolling + + SwapDoubleTriple + + SwipeDeltaX + 400 + SwipeDeltaY + 400 + TapThresholdX + 50 + TapThresholdY + 50 + USBMouseStopsTrackpad + 0 + UnitsPerMMX + 50 + UnitsPerMMY + 50 + UnsmoothInput + + VerticalScrollDivisor + 0 + WakeDelay + 1000 + ZLimit + 255 + ZoneBottom + 0 + ZoneLeft + 567 + ZoneRight + 1733 + ZoneTop + 99999 + + + ProductID + 547 + VendorID + 1452 + + + OSBundleLibraries + + com.apple.iokit.IOHIDSystem + 1.0.0b1 + com.apple.kpi.iokit + 9.0.0 + com.apple.kpi.libkern + 9.0.0 + com.apple.kpi.mach + 9.0.0 + org.rehabman.voodoo.driver.PS2Controller + 2.8.15 + + OSBundleRequired + Console + Source Code + https://github.com/RehabMan/OS-X-Voodoo-PS2-Controller + + diff --git a/VoodooPS2Trackpad/VoodooPS2Trackpad-Prefix.pch b/VoodooPS2Trackpad/VoodooPS2Trackpad-Prefix.pch new file mode 100644 index 0000000..c2f3db9 --- /dev/null +++ b/VoodooPS2Trackpad/VoodooPS2Trackpad-Prefix.pch @@ -0,0 +1,4 @@ +// +// Prefix header for all source files of the 'VoodooPS2Trackpad' target in the 'VoodooPS2Trackpad' project +// + diff --git a/VoodooPS2Trackpad/alps.cpp b/VoodooPS2Trackpad/alps.cpp new file mode 100644 index 0000000..5defcec --- /dev/null +++ b/VoodooPS2Trackpad/alps.cpp @@ -0,0 +1,3297 @@ +/* + * Copyright (c) 2002 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * The contents of this file constitute Original Code as defined in and + * are subject to the Apple Public Source License Version 1.2 (the + * "License"). You may not use this file except in compliance with the + * License. Please obtain a copy of the License + * http://www.apple.com/publicsource and read it before using this file. + * + * This Original Code and all software distributed under the License are + * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the + * License for the specific language governing rights and limitations + * under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +#include "alps.h" + +enum { + kTapEnabled = 0x01 +}; + +#define ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0])) +#define MAX(X,Y) ((X) > (Y) ? (X) : (Y)) +#define abs(x) ((x) < 0 ? -(x) : (x)) +#define BIT(x) (1 << (x)) + + +/* + * Definitions for ALPS version 3 and 4 command mode protocol + */ +#define ALPS_CMD_NIBBLE_10 0x01f2 + +#define ALPS_REG_BASE_RUSHMORE 0xc2c0 +#define ALPS_REG_BASE_V7 0xc2c0 +#define ALPS_REG_BASE_PINNACLE 0x0000 + +static const struct alps_nibble_commands alps_v3_nibble_commands[] = { + { kDP_MouseSetPoll, 0x00 }, /* 0 no send/recv */ + { kDP_SetDefaults, 0x00 }, /* 1 no send/recv */ + { kDP_SetMouseScaling2To1, 0x00 }, /* 2 no send/recv */ + { kDP_SetMouseSampleRate | 0x1000, 0x0a }, /* 3 send=1 recv=0 */ + { kDP_SetMouseSampleRate | 0x1000, 0x14 }, /* 4 ..*/ + { kDP_SetMouseSampleRate | 0x1000, 0x28 }, /* 5 ..*/ + { kDP_SetMouseSampleRate | 0x1000, 0x3c }, /* 6 ..*/ + { kDP_SetMouseSampleRate | 0x1000, 0x50 }, /* 7 ..*/ + { kDP_SetMouseSampleRate | 0x1000, 0x64 }, /* 8 ..*/ + { kDP_SetMouseSampleRate | 0x1000, 0xc8 }, /* 9 ..*/ + { kDP_CommandNibble10 | 0x0100, 0x00 }, /* a send=0 recv=1 */ + { kDP_SetMouseResolution | 0x1000, 0x00 }, /* b send=1 recv=0 */ + { kDP_SetMouseResolution | 0x1000, 0x01 }, /* c ..*/ + { kDP_SetMouseResolution | 0x1000, 0x02 }, /* d ..*/ + { kDP_SetMouseResolution | 0x1000, 0x03 }, /* e ..*/ + { kDP_SetMouseScaling1To1, 0x00 }, /* f no send/recv */ +}; + +static const struct alps_nibble_commands alps_v4_nibble_commands[] = { + { kDP_Enable, 0x00 }, /* 0 no send/recv */ + { kDP_SetDefaults, 0x00 }, /* 1 no send/recv */ + { kDP_SetMouseScaling2To1, 0x00 }, /* 2 no send/recv */ + { kDP_SetMouseSampleRate | 0x1000, 0x0a }, /* 3 send=1 recv=0 */ + { kDP_SetMouseSampleRate | 0x1000, 0x14 }, /* 4 ..*/ + { kDP_SetMouseSampleRate | 0x1000, 0x28 }, /* 5 ..*/ + { kDP_SetMouseSampleRate | 0x1000, 0x3c }, /* 6 ..*/ + { kDP_SetMouseSampleRate | 0x1000, 0x50 }, /* 7 ..*/ + { kDP_SetMouseSampleRate | 0x1000, 0x64 }, /* 8 ..*/ + { kDP_SetMouseSampleRate | 0x1000, 0xc8 }, /* 9 ..*/ + { kDP_CommandNibble10 | 0x0100, 0x00 }, /* a send=0 recv=1 */ + { kDP_SetMouseResolution | 0x1000, 0x00 }, /* b send=1 recv=0 */ + { kDP_SetMouseResolution | 0x1000, 0x01 }, /* c ..*/ + { kDP_SetMouseResolution | 0x1000, 0x02 }, /* d ..*/ + { kDP_SetMouseResolution | 0x1000, 0x03 }, /* e ..*/ + { kDP_SetMouseScaling1To1, 0x00 }, /* f no send/recv */ +}; + +static const struct alps_nibble_commands alps_v6_nibble_commands[] = { + { kDP_Enable, 0x00 }, /* 0 */ + { kDP_SetMouseSampleRate, 0x0a }, /* 1 */ + { kDP_SetMouseSampleRate, 0x14 }, /* 2 */ + { kDP_SetMouseSampleRate, 0x28 }, /* 3 */ + { kDP_SetMouseSampleRate, 0x3c }, /* 4 */ + { kDP_SetMouseSampleRate, 0x50 }, /* 5 */ + { kDP_SetMouseSampleRate, 0x64 }, /* 6 */ + { kDP_SetMouseSampleRate, 0xc8 }, /* 7 */ + { kDP_GetId, 0x00 }, /* 8 */ + { kDP_GetMouseInformation, 0x00 }, /* 9 */ + { kDP_SetMouseResolution, 0x00 }, /* a */ + { kDP_SetMouseResolution, 0x01 }, /* b */ + { kDP_SetMouseResolution, 0x02 }, /* c */ + { kDP_SetMouseResolution, 0x03 }, /* d */ + { kDP_SetMouseScaling2To1, 0x00 }, /* e */ + { kDP_SetMouseScaling1To1, 0x00 }, /* f */ +}; + + +#define ALPS_DUALPOINT 0x02 /* touchpad has trackstick */ +#define ALPS_PASS 0x04 /* device has a pass-through port */ + +#define ALPS_WHEEL 0x08 /* hardware wheel present */ +#define ALPS_FW_BK_1 0x10 /* front & back buttons present */ +#define ALPS_FW_BK_2 0x20 /* front & back buttons present */ +#define ALPS_FOUR_BUTTONS 0x40 /* 4 direction button present */ +#define ALPS_PS2_INTERLEAVED 0x80 /* 3-byte PS/2 packet interleaved with +6-byte ALPS packet */ +#define ALPS_STICK_BITS 0x100 /* separate stick button bits */ +#define ALPS_BUTTONPAD 0x200 /* device is a clickpad */ +#define ALPS_DUALPOINT_WITH_PRESSURE 0x400 /* device can report trackpoint pressure */ + + +static const struct alps_model_info alps_model_data[] = { + { { 0x32, 0x02, 0x14 }, 0x00, ALPS_PROTO_V2, 0xf8, 0xf8, ALPS_PASS | ALPS_DUALPOINT }, + /* Toshiba Salellite Pro M10 */ + { { 0x33, 0x02, 0x0a }, 0x00, ALPS_PROTO_V1, 0x88, 0xf8, 0 }, /* UMAX-530T */ + { { 0x53, 0x02, 0x0a }, 0x00, ALPS_PROTO_V2, 0xf8, 0xf8, 0 }, + { { 0x53, 0x02, 0x14 }, 0x00, ALPS_PROTO_V2, 0xf8, 0xf8, 0 }, + { { 0x60, 0x03, 0xc8 }, 0x00, ALPS_PROTO_V2, 0xf8, 0xf8, 0 }, /* HP ze1115 */ + { { 0x63, 0x02, 0x0a }, 0x00, ALPS_PROTO_V2, 0xf8, 0xf8, 0 }, + { { 0x63, 0x02, 0x14 }, 0x00, ALPS_PROTO_V2, 0xf8, 0xf8, 0 }, + { { 0x63, 0x02, 0x28 }, 0x00, ALPS_PROTO_V2, 0xf8, 0xf8, ALPS_FW_BK_2 }, /* Fujitsu Siemens S6010 */ + { { 0x63, 0x02, 0x3c }, 0x00, ALPS_PROTO_V2, 0x8f, 0x8f, ALPS_WHEEL }, /* Toshiba Satellite S2400-103 */ + { { 0x63, 0x02, 0x50 }, 0x00, ALPS_PROTO_V2, 0xef, 0xef, ALPS_FW_BK_1 }, /* NEC Versa L320 */ + { { 0x63, 0x02, 0x64 }, 0x00, ALPS_PROTO_V2, 0xf8, 0xf8, 0 }, + { { 0x63, 0x03, 0xc8 }, 0x00, ALPS_PROTO_V2, 0xf8, 0xf8, ALPS_PASS | ALPS_DUALPOINT }, + /* Dell Latitude D800 */ + { { 0x73, 0x00, 0x0a }, 0x00, ALPS_PROTO_V2, 0xf8, 0xf8, ALPS_DUALPOINT }, /* ThinkPad R61 8918-5QG */ + { { 0x73, 0x02, 0x0a }, 0x00, ALPS_PROTO_V2, 0xf8, 0xf8, 0 }, + { { 0x73, 0x02, 0x14 }, 0x00, ALPS_PROTO_V2, 0xf8, 0xf8, ALPS_FW_BK_2 }, /* Ahtec Laptop */ + { { 0x20, 0x02, 0x0e }, 0x00, ALPS_PROTO_V2, 0xf8, 0xf8, ALPS_PASS | ALPS_DUALPOINT }, + /* XXX */ + { { 0x22, 0x02, 0x0a }, 0x00, ALPS_PROTO_V2, 0xf8, 0xf8, ALPS_PASS | ALPS_DUALPOINT }, + { { 0x22, 0x02, 0x14 }, 0x00, ALPS_PROTO_V2, 0xff, 0xff, ALPS_PASS | ALPS_DUALPOINT }, + /* Dell Latitude D600 */ + /* Dell Latitude E5500, E6400, E6500, Precision M4400 */ + { { 0x62, 0x02, 0x14 }, 0x00, ALPS_PROTO_V2, 0xcf, 0xcf, + ALPS_PASS | ALPS_DUALPOINT | ALPS_PS2_INTERLEAVED }, + { { 0x73, 0x02, 0x50 }, 0x00, ALPS_PROTO_V2, 0xcf, 0xcf, ALPS_FOUR_BUTTONS }, + /* Dell Vostro 1400 */ + { { 0x52, 0x01, 0x14 }, 0x00, ALPS_PROTO_V2, 0xff, 0xff, + ALPS_PASS | ALPS_DUALPOINT | ALPS_PS2_INTERLEAVED }, + /* Toshiba Tecra A11-11L */ + { { 0x73, 0x02, 0x64 }, 0x8a, ALPS_PROTO_V4, 0x8f, 0x8f, 0 }, +}; + +// ============================================================================= +// ALPS Class Implementation ////////////////////////////////////////////////// +// ============================================================================= + +OSDefineMetaClassAndStructors(ALPS, VoodooPS2TouchPadBase); + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +ALPS *ALPS::probe(IOService *provider, SInt32 *score) { + bool success; + + // + // The driver has been instructed to verify the presence of the actual + // hardware we represent. We are guaranteed by the controller that the + // mouse clock is enabled and the mouse itself is disabled (thus it + // won't send any asynchronous mouse data that may mess up the + // responses expected by the commands we send it). + // + + _device = (ApplePS2MouseDevice *) provider; + + _device->lock(); + resetMouse(); + + if (identify() != 0) { + success = false; + } else { + success = true; + IOLog("ALPS: TouchPad driver started...\n"); + } + _device->unlock(); + + _device = 0; + + return success ? this : 0; +} + +void ALPS::restart() { + + _device->lock(); + + resetMouse(); + + identify(); + + _device->unlock(); + +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +bool ALPS::deviceSpecificInit() { + + // Setup expected packet size + priv.pktsize = priv.proto_version == ALPS_PROTO_V4 ? 8 : 6; + + if (!(this->*hw_init)()) { + goto init_fail; + } + + return true; + +init_fail: + IOLog("ALPS: Hardware initialization failed. TouchPad probably won't work\n"); + resetMouse(); + return false; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +/* Link with Base Driver */ +bool ALPS::init(OSDictionary *dict) { + if (!super::init(dict)) { + return false; + } + + // Intialize Variables + lastx=0; + lasty=0; + last_fingers=0; + xrest=0; + yrest=0; + lastbuttons=0; + + // Default Configuration + clicking=true; + rtap=true; + dragging=true; + scroll=true; + hscroll=true; + momentumscroll=true; + outzone_wt=palm=palm_wt=true; + + return true; +} + +void ALPS::stop(IOService *provider) { + + resetMouse(); + + super::stop(provider); +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +bool ALPS::resetMouse() { + TPS2Request<3> request; + + // Reset mouse + request.commands[0].command = kPS2C_SendMouseCommandAndCompareAck; + request.commands[0].inOrOut = kDP_Reset; + request.commands[1].command = kPS2C_ReadDataPort; + request.commands[1].inOrOut = 0; + request.commands[2].command = kPS2C_ReadDataPort; + request.commands[2].inOrOut = 0; + request.commandsCount = 3; + assert(request.commandsCount <= countof(request.commands)); + _device->submitRequestAndBlock(&request); + + // Verify the result + if (request.commands[1].inOrOut != kSC_Reset && request.commands[2].inOrOut != kSC_ID) { + IOLog("ALPS: Failed to reset mouse, return values did not match. [0x%02x, 0x%02x]\n", request.commands[1].inOrOut, request.commands[2].inOrOut); + return false; + } + return true; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +/* ============================================================================================== */ +/* ===============================||\\alps.c from linux 4.4//||================================== */ +/* ============================================================================================== */ + +void ALPS::alps_process_packet_v1_v2(UInt8 *packet) { + int x, y, z, ges, fin, left, right, middle, buttons = 0, fingers = 0; + int back = 0, forward = 0; + uint64_t now_abs; + + clock_get_uptime(&now_abs); + + if (priv.proto_version == ALPS_PROTO_V1) { + left = packet[2] & 0x10; + right = packet[2] & 0x08; + middle = 0; + x = packet[1] | ((packet[0] & 0x07) << 7); + y = packet[4] | ((packet[3] & 0x07) << 7); + z = packet[5]; + } else { + left = packet[3] & 1; + right = packet[3] & 2; + middle = packet[3] & 4; + x = packet[1] | ((packet[2] & 0x78) << (7 - 3)); + y = packet[4] | ((packet[3] & 0x70) << (7 - 4)); + z = packet[5]; + } + + if (priv.flags & ALPS_FW_BK_1) { + back = packet[0] & 0x10; + forward = packet[2] & 4; + } + + if (priv.flags & ALPS_FW_BK_2) { + back = packet[3] & 4; + forward = packet[2] & 4; + if ((middle = forward && back)) { + forward = back = 0; + } + } + + ges = packet[2] & 1; + fin = packet[2] & 2; + + /* To make button reporting compatible with rest of driver */ + buttons |= left ? 0x01 : 0; + buttons |= right ? 0x02 : 0; + buttons |= middle ? 0x04 : 0; + + + if ((priv.flags & ALPS_DUALPOINT) && z == 127) { + int dx, dy; + dx = x > 383 ? (x - 768) : x; + dy = -(y > 255 ? (y - 512) : y); + + dispatchRelativePointerEventX(dx, dy, buttons, now_abs); + return; + } + + /* Some models have separate stick button bits */ + if (priv.flags & ALPS_STICK_BITS) { + left |= packet[0] & 1; + right |= packet[0] & 2; + middle |= packet[0] & 4; + } + + /* Convert hardware tap to a reasonable Z value */ + if (ges && !fin) { + z = 40; + } + + /* + * A "tap and drag" operation is reported by the hardware as a transition + * from (!fin && ges) to (fin && ges). This should be translated to the + * sequence Z>0, Z==0, Z>0, so the Z==0 event has to be generated manually. + */ + if (ges && fin && !priv.prev_fin) { + touchmode = MODE_DRAG; + } + priv.prev_fin = fin; + + if (z > 30) { + fingers = 1; + } + + if (z < 25) { + fingers = 0; + } + + dispatchEventsWithInfo(x, y, z, fingers, buttons); + + if (priv.flags & ALPS_WHEEL) { + int scrollAmount = ((packet[2] << 1) & 0x08) - ((packet[0] >> 4) & 0x07); + if (scrollAmount) { + dispatchScrollWheelEventX(scrollAmount, 0, 0, now_abs); + } + } +} + +static void alps_get_bitmap_points(unsigned int map, + struct alps_bitmap_point *low, + struct alps_bitmap_point *high, + int *fingers) +{ + struct alps_bitmap_point *point; + int i, bit, prev_bit = 0; + + point = low; + for (i = 0; map != 0; i++, map >>= 1) { + bit = map & 1; + if (bit) { + if (!prev_bit) { + point->start_bit = i; + point->num_bits = 0; + (*fingers)++; + } + point->num_bits++; + } else { + if (prev_bit) + point = high; + } + prev_bit = bit; + } +} + +/* + * Process bitmap data from semi-mt protocols. Returns the number of + * fingers detected. A return value of 0 means at least one of the + * bitmaps was empty. + * + * The bitmaps don't have enough data to track fingers, so this function + * only generates points representing a bounding box of all contacts. + * These points are returned in fields->mt when the return value + * is greater than 0. + */ +int ALPS::alps_process_bitmap(struct alps_data *priv, + struct alps_fields *fields) +{ + + int i, fingers_x = 0, fingers_y = 0, fingers, closest; + struct alps_bitmap_point x_low = {0,}, x_high = {0,}; + struct alps_bitmap_point y_low = {0,}, y_high = {0,}; + struct input_mt_pos corner[4]; + + + if (!fields->x_map || !fields->y_map) { + return 0; + } + + alps_get_bitmap_points(fields->x_map, &x_low, &x_high, &fingers_x); + alps_get_bitmap_points(fields->y_map, &y_low, &y_high, &fingers_y); + + /* + * Fingers can overlap, so we use the maximum count of fingers + * on either axis as the finger count. + */ + fingers = max(fingers_x, fingers_y); + + /* + * If an axis reports only a single contact, we have overlapping or + * adjacent fingers. Divide the single contact between the two points. + */ + if (fingers_x == 1) { + i = x_low.num_bits / 2; + x_low.num_bits = x_low.num_bits - i; + x_high.start_bit = x_low.start_bit + i; + x_high.num_bits = max(i, 1); + } + + if (fingers_y == 1) { + i = y_low.num_bits / 2; + y_low.num_bits = y_low.num_bits - i; + y_high.start_bit = y_low.start_bit + i; + y_high.num_bits = max(i, 1); + } + + /* top-left corner */ + corner[0].x = (priv->x_max * (2 * x_low.start_bit + x_low.num_bits - 1)) / + (2 * (priv->x_bits - 1)); + corner[0].y = (priv->y_max * (2 * y_low.start_bit + y_low.num_bits - 1)) / + (2 * (priv->y_bits - 1)); + + /* top-right corner */ + corner[1].x = (priv->x_max * (2 * x_high.start_bit + x_high.num_bits - 1)) / + (2 * (priv->x_bits - 1)); + corner[1].y = (priv->y_max * (2 * y_low.start_bit + y_low.num_bits - 1)) / + (2 * (priv->y_bits - 1)); + + /* bottom-right corner */ + corner[2].x = (priv->x_max * (2 * x_high.start_bit + x_high.num_bits - 1)) / + (2 * (priv->x_bits - 1)); + corner[2].y = (priv->y_max * (2 * y_high.start_bit + y_high.num_bits - 1)) / + (2 * (priv->y_bits - 1)); + + /* bottom-left corner */ + corner[3].x = (priv->x_max * (2 * x_low.start_bit + x_low.num_bits - 1)) / + (2 * (priv->x_bits - 1)); + corner[3].y = (priv->y_max * (2 * y_high.start_bit + y_high.num_bits - 1)) / + (2 * (priv->y_bits - 1)); + + /* x-bitmap order is reversed on v5 touchpads */ + if (priv->proto_version == ALPS_PROTO_V5) { + for (i = 0; i < 4; i++) + corner[i].x = priv->x_max - corner[i].x; + } + + /* y-bitmap order is reversed on v3 and v4 touchpads */ + if (priv->proto_version == ALPS_PROTO_V3 || priv->proto_version == ALPS_PROTO_V4) { + for (i = 0; i < 4; i++) + corner[i].y = priv->y_max - corner[i].y; + } + + /* + * We only select a corner for the second touch once per 2 finger + * touch sequence to avoid the chosen corner (and thus the coordinates) + * jumping around when the first touch is in the middle. + */ + if (priv->second_touch == -1) { + /* Find corner closest to our st coordinates */ + closest = 0x7fffffff; + for (i = 0; i < 4; i++) { + int dx = fields->st.x - corner[i].x; + int dy = fields->st.y - corner[i].y; + int distance = dx * dx + dy * dy; + + if (distance < closest) { + priv->second_touch = i; + closest = distance; + } + } + /* And select the opposite corner to use for the 2nd touch */ + priv->second_touch = (priv->second_touch + 2) % 4; + } + + fields->mt[0] = fields->st; + fields->mt[1] = corner[priv->second_touch]; + + //IOLog("ALPS: Process Bitmap, Corner=%d, Fingers=%d, x1=%d, x2=%d, y1=%d, y2=%d\n", priv->second_touch, fingers, fields->mt[0].x, fields->mt[1].x, fields->mt[0].y, fields->mt[1].y); + return fingers; +} + +void ALPS::alps_process_trackstick_packet_v3(UInt8 *packet) { + int x, y, z, left, right, middle; + uint64_t now_abs; + UInt32 buttons = 0, raw_buttons = 0; + + /* It should be a DualPoint when received trackstick packet */ + if (!(priv.flags & ALPS_DUALPOINT)) { + return; + } + + /* Sanity check packet */ + if (!(packet[0] & 0x40)) { + DEBUG_LOG("ps2: bad trackstick packet, disregarding...\n"); + return; + } + + /* There is a special packet that seems to indicate the end + * of a stream of trackstick data. Filter these out + */ + if (packet[1] == 0x7f && packet[2] == 0x7f && packet[3] == 0x7f) { + return; + } + + x = (SInt8) (((packet[0] & 0x20) << 2) | (packet[1] & 0x7f)); + y = (SInt8) (((packet[0] & 0x10) << 3) | (packet[2] & 0x7f)); + z = (packet[4] & 0x7c) >> 2; + + /* Prevent pointer jump on finger lift */ + if ((abs(x) >= 0x7f) && (abs(y) >= 0x7f)) { + x = y = 0; + } + + /* + * The x and y values tend to be quite large, and when used + * alone the trackstick is difficult to use. Scale them down + * to compensate. + */ + x /= 3; + y /= 3; + + /* To get proper movement direction */ + y = -y; + + clock_get_uptime(&now_abs); + + /* + * Most ALPS models report the trackstick buttons in the touchpad + * packets, but a few report them here. No reliable way has been + * found to differentiate between the models upfront, so we enable + * the quirk in response to seeing a button press in the trackstick + * packet. + */ + left = packet[3] & 0x01; + right = packet[3] & 0x02; + middle = packet[3] & 0x04; + + if (!(priv.quirks & ALPS_QUIRK_TRACKSTICK_BUTTONS) && + (left || middle || right)) { + priv.quirks |= ALPS_QUIRK_TRACKSTICK_BUTTONS; + } + + if (priv.quirks & ALPS_QUIRK_TRACKSTICK_BUTTONS) { + raw_buttons |= left ? 0x01 : 0; + raw_buttons |= right ? 0x02 : 0; + raw_buttons |= middle ? 0x04 : 0; + } + + /* Button status can appear in normal packet */ + if (0 == raw_buttons) { + buttons = lastbuttons; + } else { + buttons = raw_buttons; + lastbuttons = buttons; + } + + /* If middle button is pressed, switch to scroll mode. Else, move pointer normally */ + if (0 == (buttons & 0x04)) { + dispatchRelativePointerEventX(x, y, buttons, now_abs); + } else { + dispatchScrollWheelEventX(-y, -x, 0, now_abs); + } +} + +bool ALPS::alps_decode_buttons_v3(struct alps_fields *f, unsigned char *p) { + f->left = !!(p[3] & 0x01); + f->right = !!(p[3] & 0x02); + f->middle = !!(p[3] & 0x04); + + f->ts_left = !!(p[3] & 0x10); + f->ts_right = !!(p[3] & 0x20); + f->ts_middle = !!(p[3] & 0x40); + return true; +} + +bool ALPS::alps_decode_pinnacle(struct alps_fields *f, UInt8 *p) { + f->first_mp = !!(p[4] & 0x40); + f->is_mp = !!(p[0] & 0x40); + + if (f->is_mp) { + f->fingers = (p[5] & 0x3) + 1; + f->x_map = ((p[4] & 0x7e) << 8) | + ((p[1] & 0x7f) << 2) | + ((p[0] & 0x30) >> 4); + f->y_map = ((p[3] & 0x70) << 4) | + ((p[2] & 0x7f) << 1) | + (p[4] & 0x01); + } else { + f->st.x = ((p[1] & 0x7f) << 4) | ((p[4] & 0x30) >> 2) | + ((p[0] & 0x30) >> 4); + f->st.y = ((p[2] & 0x7f) << 4) | (p[4] & 0x0f); + f->pressure = p[5] & 0x7f; + + alps_decode_buttons_v3(f, p); + } + return true; +} + +bool ALPS::alps_decode_rushmore(struct alps_fields *f, UInt8 *p) { + f->first_mp = !!(p[4] & 0x40); + f->is_mp = !!(p[5] & 0x40); + + if (f->is_mp) { + f->fingers = max((p[5] & 0x3), ((p[5] >> 2) & 0x3)) + 1; + f->x_map = ((p[5] & 0x10) << 11) | + ((p[4] & 0x7e) << 8) | + ((p[1] & 0x7f) << 2) | + ((p[0] & 0x30) >> 4); + f->y_map = ((p[5] & 0x20) << 6) | + ((p[3] & 0x70) << 4) | + ((p[2] & 0x7f) << 1) | + (p[4] & 0x01); + } else { + f->st.x = ((p[1] & 0x7f) << 4) | ((p[4] & 0x30) >> 2) | + ((p[0] & 0x30) >> 4); + f->st.y = ((p[2] & 0x7f) << 4) | (p[4] & 0x0f); + f->pressure = p[5] & 0x7f; + + alps_decode_buttons_v3(f, p); + } + return true; +} + +bool ALPS::alps_decode_dolphin(struct alps_fields *f, UInt8 *p) { + uint64_t palm_data = 0; + + f->first_mp = !!(p[0] & 0x02); + f->is_mp = !!(p[0] & 0x20); + + if (!f->is_mp) { + f->st.x = ((p[1] & 0x7f) | ((p[4] & 0x0f) << 7)); + f->st.y = ((p[2] & 0x7f) | ((p[4] & 0xf0) << 3)); + f->pressure = (p[0] & 4) ? 0 : p[5] & 0x7f; + alps_decode_buttons_v3(f, p); + } else { + f->fingers = ((p[0] & 0x6) >> 1 | + (p[0] & 0x10) >> 2); + + palm_data = (p[1] & 0x7f) | + ((p[2] & 0x7f) << 7) | + ((p[4] & 0x7f) << 14) | + ((p[5] & 0x7f) << 21) | + ((p[3] & 0x07) << 28) | + (((uint64_t)p[3] & 0x70) << 27) | + (((uint64_t)p[0] & 0x01) << 34); + + /* Y-profile is stored in P(0) to p(n-1), n = y_bits; */ + f->y_map = palm_data & (BIT(priv.y_bits) - 1); + + /* X-profile is stored in p(n) to p(n+m-1), m = x_bits; */ + f->x_map = (palm_data >> priv.y_bits) & + (BIT(priv.x_bits) - 1); + } + return true; +} + +void ALPS::alps_process_touchpad_packet_v3_v5(UInt8 *packet) { + //ffff + int fingers = 0, buttons = 0; + struct alps_fields f; + + memset(&f, 0, sizeof(f)); + + (this->*decode_fields)(&f, packet); + /* + * There's no single feature of touchpad position and bitmap packets + * that can be used to distinguish between them. We rely on the fact + * that a bitmap packet should always follow a position packet with + * bit 6 of packet[4] set. + */ + if (priv.multi_packet) { + /* + * Sometimes a position packet will indicate a multi-packet + * sequence, but then what follows is another position + * packet. Check for this, and when it happens process the + * position packet as usual. + */ + if (f.is_mp) { + fingers = f.fingers; + /* + * Bitmap processing uses position packet's coordinate + * data, so we need to do decode it first. + */ + (this->*decode_fields)(&f, priv.multi_data); + if (alps_process_bitmap(&priv, &f) == 0) { + fingers = 0; /* Use st data */ + } + } else { + priv.multi_packet = 0; + } + } + + /* + * Bit 6 of byte 0 is not usually set in position packets. The only + * times it seems to be set is in situations where the data is + * suspect anyway, e.g. a palm resting flat on the touchpad. Given + * this combined with the fact that this bit is useful for filtering + * out misidentified bitmap packets, we reject anything with this + * bit set. + */ + if (f.is_mp) { + return; + } + + if (!priv.multi_packet && (f.first_mp)) { + priv.multi_packet = 1; + memcpy(priv.multi_data, packet, sizeof(priv.multi_data)); + return; + } + + priv.multi_packet = 0; + + /* + * Sometimes the hardware sends a single packet with z = 0 + * in the middle of a stream. Real releases generate packets + * with x, y, and z all zero, so these seem to be flukes. + * Ignore them. + */ + if (f.st.x && f.st.y && !f.pressure) { + //return; //Dr Hurt: This causes jitter + } + + /* Use st data when we don't have mt data */ + if (fingers < 2) { + f.mt[0].x = f.st.x; + f.mt[0].y = f.st.y; + fingers = f.pressure > 0 ? 1 : 0; + priv.second_touch = -1; + } + + buttons |= f.left ? 0x01 : 0; + buttons |= f.right ? 0x02 : 0; + buttons |= f.middle ? 0x04 : 0; + + if ((priv.flags & ALPS_DUALPOINT) && + !(priv.quirks & ALPS_QUIRK_TRACKSTICK_BUTTONS)) { + buttons |= f.ts_left ? 0x01 : 0; + buttons |= f.ts_right ? 0x02 : 0; + buttons |= f.ts_middle ? 0x04 : 0; + } + + /* Reverse y co-ordinates to have 0 at bottom for gestures to work */ + f.mt[0].y = priv.y_max - f.mt[0].y; + f.mt[1].y = priv.y_max - f.mt[1].y; + + /* Ignore 1 finger events after 2 finger scroll to prevent jitter */ + if (last_fingers == 2 && fingers == 1) { + fingers = 2; + } + + dispatchEventsWithInfo(f.mt[0].x, f.mt[0].y, f.pressure, fingers, buttons); +} + +void ALPS::alps_process_packet_v3(UInt8 *packet) { + /* + * v3 protocol packets come in three types, two representing + * touchpad data and one representing trackstick data. + * Trackstick packets seem to be distinguished by always + * having 0x3f in the last byte. This value has never been + * observed in the last byte of either of the other types + * of packets. + */ + if (packet[5] == 0x3f) { + alps_process_trackstick_packet_v3(packet); + return; + } + + alps_process_touchpad_packet_v3_v5(packet); +} + +void ALPS::alps_process_packet_v6(UInt8 *packet) +{ + int x, y, z, left, right, middle; + int fingers = 0; + int buttons = 0; + + uint64_t now_abs; + clock_get_uptime(&now_abs); + + /* + * We can use Byte5 to distinguish if the packet is from Touchpad + * or Trackpoint. + * Touchpad: 0 - 0x7E + * Trackpoint: 0x7F + */ + if (packet[5] == 0x7F) { + /* It should be a DualPoint when received Trackpoint packet */ + if (!(priv.flags & ALPS_DUALPOINT)) { + return; + } + + /* Trackpoint packet */ + x = packet[1] | ((packet[3] & 0x20) << 2); + y = packet[2] | ((packet[3] & 0x40) << 1); + z = packet[4]; + left = packet[3] & 0x01; + right = packet[3] & 0x02; + middle = packet[3] & 0x04; + + buttons |= left ? 0x01 : 0; + buttons |= right ? 0x02 : 0; + buttons |= middle ? 0x04 : 0; + + /* To prevent the cursor jump when finger lifted */ + if (x == 0x7F && y == 0x7F && z == 0x7F) + x = y = z = 0; + + /* Divide 4 since trackpoint's speed is too fast */ + dispatchRelativePointerEventX(x / 4, y / 4, buttons, now_abs); + return; + } + + /* Touchpad packet */ + x = packet[1] | ((packet[3] & 0x78) << 4); + y = packet[2] | ((packet[4] & 0x78) << 4); + z = packet[5]; + left = packet[3] & 0x01; + right = packet[3] & 0x02; + + fingers = z > 0 ? 1 : 0; + + buttons |= left ? 0x01 : 0; + buttons |= right ? 0x02 : 0; + + dispatchEventsWithInfo(x, y, z, fingers, buttons); +} + +void ALPS::alps_process_packet_v4(UInt8 *packet) { + SInt32 offset; + SInt32 fingers = 0; + UInt32 buttons = 0; + struct alps_fields f; + + /* + * v4 has a 6-byte encoding for bitmap data, but this data is + * broken up between 3 normal packets. Use priv.multi_packet to + * track our position in the bitmap packet. + */ + if (packet[6] & 0x40) { + /* sync, reset position */ + priv.multi_packet = 0; + } + + if (priv.multi_packet > 2) { + return; + } + + offset = 2 * priv.multi_packet; + priv.multi_data[offset] = packet[6]; + priv.multi_data[offset + 1] = packet[7]; + + f.left = packet[4] & 0x01; + f.right = packet[4] & 0x02; + + f.st.x = ((packet[1] & 0x7f) << 4) | ((packet[3] & 0x30) >> 2) | + ((packet[0] & 0x30) >> 4); + f.st.y = ((packet[2] & 0x7f) << 4) | (packet[3] & 0x0f); + f.pressure = packet[5] & 0x7f; + + if (++priv.multi_packet > 2) { + priv.multi_packet = 0; + + f.x_map = ((priv.multi_data[2] & 0x1f) << 10) | + ((priv.multi_data[3] & 0x60) << 3) | + ((priv.multi_data[0] & 0x3f) << 2) | + ((priv.multi_data[1] & 0x60) >> 5); + f.y_map = ((priv.multi_data[5] & 0x01) << 10) | + ((priv.multi_data[3] & 0x1f) << 5) | + (priv.multi_data[1] & 0x1f); + + fingers = alps_process_bitmap(&priv, &f); + + } + + buttons |= f.left ? 0x01 : 0; + buttons |= f.right ? 0x02 : 0; + + dispatchEventsWithInfo(f.st.x, f.st.y, f.pressure, fingers, buttons); +} + +unsigned char ALPS::alps_get_packet_id_v7(UInt8 *byte) +{ + unsigned char packet_id; + + if (byte[4] & 0x40) + packet_id = V7_PACKET_ID_TWO; + else if (byte[4] & 0x01) + packet_id = V7_PACKET_ID_MULTI; + else if ((byte[0] & 0x10) && !(byte[4] & 0x43)) + packet_id = V7_PACKET_ID_NEW; + else if (byte[1] == 0x00 && byte[4] == 0x00) + packet_id = V7_PACKET_ID_IDLE; + else + packet_id = V7_PACKET_ID_UNKNOWN; + + return packet_id; +} + +void ALPS::alps_get_finger_coordinate_v7(struct input_mt_pos *mt, + UInt8 *pkt, + UInt8 pkt_id) +{ + mt[0].x = ((pkt[2] & 0x80) << 4); + mt[0].x |= ((pkt[2] & 0x3F) << 5); + mt[0].x |= ((pkt[3] & 0x30) >> 1); + mt[0].x |= (pkt[3] & 0x07); + mt[0].y = (pkt[1] << 3) | (pkt[0] & 0x07); + + mt[1].x = ((pkt[3] & 0x80) << 4); + mt[1].x |= ((pkt[4] & 0x80) << 3); + mt[1].x |= ((pkt[4] & 0x3F) << 4); + mt[1].y = ((pkt[5] & 0x80) << 3); + mt[1].y |= ((pkt[5] & 0x3F) << 4); + + switch (pkt_id) { + case V7_PACKET_ID_TWO: + mt[1].x &= ~0x000F; + mt[1].y |= 0x000F; + /* Detect false-postive touches where x & y report max value */ + if (mt[1].y == 0x7ff && mt[1].x == 0xff0) + mt[1].x = 0; + /* y gets set to 0 at the end of this function */ + break; + + case V7_PACKET_ID_MULTI: + mt[1].x &= ~0x003F; + mt[1].y &= ~0x0020; + mt[1].y |= ((pkt[4] & 0x02) << 4); + mt[1].y |= 0x001F; + break; + + case V7_PACKET_ID_NEW: + mt[1].x &= ~0x003F; + mt[1].x |= (pkt[0] & 0x20); + mt[1].y |= 0x000F; + break; + } + + mt[0].y = 0x7FF - mt[0].y; + mt[1].y = 0x7FF - mt[1].y; +} + +int ALPS::alps_get_mt_count(struct input_mt_pos *mt) +{ + int i, fingers = 0; + + for (i = 0; i < MAX_TOUCHES; i++) { + if (mt[i].x != 0 || mt[i].y != 0) + fingers++; + } + + return fingers; +} + +bool ALPS::alps_decode_packet_v7(struct alps_fields *f, UInt8 *p){ + //IOLog("Decode V7 touchpad Packet... 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x\n", p[0], p[1], p[2], p[3], p[4], p[5]); + + unsigned char pkt_id; + + pkt_id = alps_get_packet_id_v7(p); + if (pkt_id == V7_PACKET_ID_IDLE) + return true; + if (pkt_id == V7_PACKET_ID_UNKNOWN) + return false; + + /* + * NEW packets are send to indicate a discontinuity in the finger + * coordinate reporting. Specifically a finger may have moved from + * slot 0 to 1 or vice versa. INPUT_MT_TRACK takes care of this for + * us. + * + * NEW packets have 3 problems: + * 1) They do not contain middle / right button info (on non clickpads) + * this can be worked around by preserving the old button state + * 2) They do not contain an accurate fingercount, and they are + * typically send when the number of fingers changes. We cannot use + * the old finger count as that may mismatch with the amount of + * touch coordinates we've available in the NEW packet + * 3) Their x data for the second touch is inaccurate leading to + * a possible jump of the x coordinate by 16 units when the first + * non NEW packet comes in + * Since problems 2 & 3 cannot be worked around, just ignore them. + */ + if (pkt_id == V7_PACKET_ID_NEW) + return true; + + alps_get_finger_coordinate_v7(f->mt, p, pkt_id); + + if (pkt_id == V7_PACKET_ID_TWO) + f->fingers = alps_get_mt_count(f->mt); + else /* pkt_id == V7_PACKET_ID_MULTI */ + f->fingers = 3 + (p[5] & 0x03); + + f->left = (p[0] & 0x80) >> 7; + if (priv.flags & ALPS_BUTTONPAD) { + if (p[0] & 0x20) + f->fingers++; + if (p[0] & 0x10) + f->fingers++; + } else { + f->right = (p[0] & 0x20) >> 5; + f->middle = (p[0] & 0x10) >> 4; + } + + /* Sometimes a single touch is reported in mt[1] rather then mt[0] */ + if (f->fingers == 1 && f->mt[0].x == 0 && f->mt[0].y == 0) { + f->mt[0].x = f->mt[1].x; + f->mt[0].y = f->mt[1].y; + f->mt[1].x = 0; + f->mt[1].y = 0; + } + return true; +} + +void ALPS::alps_process_trackstick_packet_v7(UInt8 *packet) +{ + int x, y, z, left, right, middle; + int buttons = 0; + + /* It should be a DualPoint when received trackstick packet */ + if (!(priv.flags & ALPS_DUALPOINT)) { + IOLog("ALPS: Rejected trackstick packet from non DualPoint device"); + return; + } + + x = ((packet[2] & 0xbf)) | ((packet[3] & 0x10) << 2); + y = (packet[3] & 0x07) | (packet[4] & 0xb8) | + ((packet[3] & 0x20) << 1); + z = (packet[5] & 0x3f) | ((packet[3] & 0x80) >> 1); + + left = (packet[1] & 0x01); + right = (packet[1] & 0x02) >> 1; + middle = (packet[1] & 0x04) >> 2; + + buttons |= left ? 0x01 : 0; + buttons |= right ? 0x02 : 0; + buttons |= middle ? 0x04 : 0; + + //TODO: V7 Trackstick: Someone with the hardware needs to debug this. + //dispatchRelativePointerEventX(x, y, 0, now_abs); +} + +void ALPS::alps_process_touchpad_packet_v7(UInt8 *packet){ + int fingers = 0; + UInt32 buttons = 0; + struct alps_fields f; + + memset(&f, 0, sizeof(alps_fields)); + + if (!(this->*decode_fields)(&f, packet)) + return; + + buttons |= f.left ? 0x01 : 0; + buttons |= f.right ? 0x02 : 0; + buttons |= f.middle ? 0x04 : 0; + + if ((priv.flags & ALPS_DUALPOINT) && + !(priv.quirks & ALPS_QUIRK_TRACKSTICK_BUTTONS)) { + buttons |= f.ts_left ? 0x01 : 0; + buttons |= f.ts_right ? 0x02 : 0; + buttons |= f.ts_middle ? 0x04 : 0; + } + + fingers = f.fingers; + + /* Reverse y co-ordinates to have 0 at bottom for gestures to work */ + f.mt[0].y = priv.y_max - f.mt[0].y; + f.mt[1].y = priv.y_max - f.mt[1].y; + + //Hack because V7 doesn't report pressure + /*if (fingers != 0 && (f.mt[0].x != 0 && f.mt[0].y != 0)) { + f.pressure = 40; + } else { + f.pressure = 0; + }*/ + + f.pressure = fingers > 0 ? 40 : 0; + + dispatchEventsWithInfo(f.mt[0].x, f.mt[0].y, f.pressure, fingers, buttons); +} + +void ALPS::alps_process_packet_v7(UInt8 *packet){ + if (packet[0] == 0x48 && (packet[4] & 0x47) == 0x06) + alps_process_trackstick_packet_v7(packet); + else + alps_process_touchpad_packet_v7(packet); +} + +unsigned char ALPS::alps_get_pkt_id_ss4_v2(UInt8 *byte) +{ + unsigned char pkt_id = SS4_PACKET_ID_IDLE; + + switch (byte[3] & 0x30) { + case 0x00: + if (byte[0] == 0x18 && byte[1] == 0x10 && byte[2] == 0x00 && + (byte[3] & 0x88) == 0x08 && byte[4] == 0x10 && + byte[5] == 0x00) { + pkt_id = SS4_PACKET_ID_IDLE; + } else { + pkt_id = SS4_PACKET_ID_ONE; + } + break; + case 0x10: + /* two-finger finger positions */ + pkt_id = SS4_PACKET_ID_TWO; + break; + case 0x20: + /* stick pointer */ + pkt_id = SS4_PACKET_ID_STICK; + break; + case 0x30: + /* third and fourth finger positions */ + pkt_id = SS4_PACKET_ID_MULTI; + break; + } + + return pkt_id; +} + +bool ALPS::alps_decode_ss4_v2(struct alps_fields *f, UInt8 *p){ + + //struct alps_data *priv; + unsigned char pkt_id; + unsigned int no_data_x, no_data_y; + uint64_t now_abs; + clock_get_uptime(&now_abs); + + pkt_id = alps_get_pkt_id_ss4_v2(p); + + /* Current packet is 1Finger coordinate packet */ + switch (pkt_id) { + case SS4_PACKET_ID_ONE: + f->mt[0].x = SS4_1F_X_V2(p); + f->mt[0].y = SS4_1F_Y_V2(p); + f->pressure = ((SS4_1F_Z_V2(p)) * 2) & 0x7f; + /* + * When a button is held the device will give us events + * with x, y, and pressure of 0. This causes annoying jumps + * if a touch is released while the button is held. + * Handle this by claiming zero contacts. + */ + f->fingers = f->pressure > 0 ? 1 : 0; + f->first_mp = 0; + f->is_mp = 0; + break; + + case SS4_PACKET_ID_TWO: + if (priv.flags & ALPS_BUTTONPAD) { + f->mt[0].x = SS4_BTL_MF_X_V2(p, 0); + f->mt[0].y = SS4_BTL_MF_Y_V2(p, 0); + f->mt[1].x = SS4_BTL_MF_X_V2(p, 1); + f->mt[1].y = SS4_BTL_MF_Y_V2(p, 1); + } else { + f->mt[0].x = SS4_STD_MF_X_V2(p, 0); + f->mt[0].y = SS4_STD_MF_Y_V2(p, 0); + f->mt[1].x = SS4_STD_MF_X_V2(p, 1); + f->mt[1].y = SS4_STD_MF_Y_V2(p, 1); + } + f->pressure = SS4_MF_Z_V2(p, 0) ? 0x30 : 0; + + if (SS4_IS_MF_CONTINUE(p)) { + f->first_mp = 1; + } else { + f->fingers = 2; + f->first_mp = 0; + } + f->is_mp = 0; + + break; + + case SS4_PACKET_ID_MULTI: + if (priv.flags & ALPS_BUTTONPAD) { + f->mt[2].x = SS4_BTL_MF_X_V2(p, 0); + f->mt[2].y = SS4_BTL_MF_Y_V2(p, 0); + f->mt[3].x = SS4_BTL_MF_X_V2(p, 1); + f->mt[3].y = SS4_BTL_MF_Y_V2(p, 1); + no_data_x = SS4_MFPACKET_NO_AX_BL; + no_data_y = SS4_MFPACKET_NO_AY_BL; + } else { + f->mt[2].x = SS4_STD_MF_X_V2(p, 0); + f->mt[2].y = SS4_STD_MF_Y_V2(p, 0); + f->mt[3].x = SS4_STD_MF_X_V2(p, 1); + f->mt[3].y = SS4_STD_MF_Y_V2(p, 1); + no_data_x = SS4_MFPACKET_NO_AX; + no_data_y = SS4_MFPACKET_NO_AY; + } + + f->first_mp = 0; + f->is_mp = 1; + + if (SS4_IS_5F_DETECTED(p)) { + f->fingers = 5; + } else if (f->mt[3].x == no_data_x && + f->mt[3].y == no_data_y) { + f->mt[3].x = 0; + f->mt[3].y = 0; + f->fingers = 3; + } else { + f->fingers = 4; + } + break; + + case SS4_PACKET_ID_STICK: + /* + * x, y, and pressure are decoded in + * alps_process_packet_ss4_v2() + */ + f->first_mp = 0; + f->is_mp = 0; + break; + + case SS4_PACKET_ID_IDLE: + default: + memset(f, 0, sizeof(struct alps_fields)); + break; + } + + /* handle buttons */ + if (pkt_id == SS4_PACKET_ID_STICK) { + f->ts_left = !!(SS4_BTN_V2(p) & 0x01); + if (!(priv.flags & ALPS_BUTTONPAD)) { + f->ts_right = !!(SS4_BTN_V2(p) & 0x02); + f->ts_middle = !!(SS4_BTN_V2(p) & 0x04); + } + } else { + f->left = !!(SS4_BTN_V2(p) & 0x01); + if (!(priv.flags & ALPS_BUTTONPAD)) { + f->right = !!(SS4_BTN_V2(p) & 0x02); + f->middle = !!(SS4_BTN_V2(p) & 0x04); + } + } + return true; +} + +void ALPS::alps_process_packet_ss4_v2(UInt8 *packet) { + int buttons = 0; + struct alps_fields f; + int x, y, pressure; + + uint64_t now_abs; + clock_get_uptime(&now_abs); + + memset(&f, 0, sizeof(struct alps_fields)); + (this->*decode_fields)(&f, packet); + if (priv.multi_packet) { + /* + * Sometimes the first packet will indicate a multi-packet + * sequence, but sometimes the next multi-packet would not + * come. Check for this, and when it happens process the + * position packet as usual. + */ + if (f.is_mp) { + /* Now process the 1st packet */ + (this->*decode_fields)(&f, priv.multi_data); + } else { + priv.multi_packet = 0; + } + } + + /* + * "f.is_mp" would always be '0' after merging the 1st and 2nd packet. + * When it is set, it means 2nd packet comes without 1st packet come. + */ + if (f.is_mp) { + return; + } + + /* Save the first packet */ + if (!priv.multi_packet && f.first_mp) { + priv.multi_packet = 1; + memcpy(priv.multi_data, packet, sizeof(priv.multi_data)); + return; + } + + priv.multi_packet = 0; + + /* Report trackstick */ + if (alps_get_pkt_id_ss4_v2(packet) == SS4_PACKET_ID_STICK) { + if (!(priv.flags & ALPS_DUALPOINT)) { + IOLog("ALPS: Rejected trackstick packet from non DualPoint device\n"); + return; + } + + x = (((packet[0] & 1) << 7) | (packet[1] & 0x7f)); + y = (((packet[3] & 1) << 7) | (packet[2] & 0x7f)); + pressure = (packet[4] & 0x7f); + + buttons |= f.ts_left ? 0x01 : 0; + buttons |= f.ts_right ? 0x02 : 0; + buttons |= f.ts_middle ? 0x04 : 0; + + if ((abs(x) >= 0x7f) || (abs(y) >= 0x7f)) { + return; + } + + //TODO: V8 Trackstick: Someone with the hardware needs to debug this. + //IOLog("ALPS: Trackstick report: X=%d, Y=%d, Z=%d, buttons=%d\n", x, y, pressure, buttons); + //dispatchRelativePointerEventX(x, y, 0, now_abs); + return; + } + + /* Report touchpad */ + buttons |= f.left ? 0x01 : 0; + buttons |= f.right ? 0x02 : 0; + buttons |= f.middle ? 0x04 : 0; + + /* Reverse y co-ordinates to have 0 at bottom for gestures to work */ + f.mt[0].y = priv.y_max - f.mt[0].y; + f.mt[1].y = priv.y_max - f.mt[1].y; + + dispatchEventsWithInfo(f.mt[0].x, f.mt[0].y, f.pressure, f.fingers, buttons); +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +void ALPS::setTouchPadEnable(bool enable) { + DEBUG_LOG("setTouchpadEnable enter\n"); + // + // Instructs the trackpad to start or stop the reporting of data packets. + // It is safe to issue this request from the interrupt/completion context. + // + + if (enable) { + initTouchPad(); + } else { + // to disable just reset the mouse + resetMouse(); + } +} + +PS2InterruptResult ALPS::interruptOccurred(UInt8 data) { + // + // This will be invoked automatically from our device when asynchronous + // events need to be delivered. Process the trackpad data. Do NOT issue + // any BLOCKING commands to our device in this context. + // + + UInt8 *packet = _ringBuffer.head(); + + /* Save first packet */ + if (0 == _packetByteCount) { + packet[0] = data; + } + + /* Reset PSMOUSE_BAD_DATA flag */ + priv.PSMOUSE_BAD_DATA = false; + + /* + * Check if we are dealing with a bare PS/2 packet, presumably from + * a device connected to the external PS/2 port. Because bare PS/2 + * protocol does not have enough constant bits to self-synchronize + * properly we only do this if the device is fully synchronized. + * Can not distinguish V8's first byte from PS/2 packet's + */ + if (priv.proto_version != ALPS_PROTO_V8 && + (packet[0] & 0xc8) == 0x08) { + if (_packetByteCount == 3) { + //dispatchRelativePointerEventWithPacket(packet, kPacketLengthSmall); //Dr Hurt: allow this? + priv.PSMOUSE_BAD_DATA = true; + _ringBuffer.advanceHead(priv.pktsize); + return kPS2IR_packetReady; + } + packet[_packetByteCount++] = data; + return kPS2IR_packetBuffering; + } + + /* Check for PS/2 packet stuffed in the middle of ALPS packet. */ + if ((priv.flags & ALPS_PS2_INTERLEAVED) && + _packetByteCount >= 4 && (packet[3] & 0x0f) == 0x0f) { + priv.PSMOUSE_BAD_DATA = true; + _ringBuffer.advanceHead(priv.pktsize); + return kPS2IR_packetReady; + } + + /* alps_is_valid_first_byte */ + if ((packet[0] & priv.mask0) != priv.byte0) { + priv.PSMOUSE_BAD_DATA = true; + _ringBuffer.advanceHead(priv.pktsize); + return kPS2IR_packetReady; + } + + /* Bytes 2 - pktsize should have 0 in the highest bit */ + if (priv.proto_version < ALPS_PROTO_V5 && + _packetByteCount >= 2 && _packetByteCount <= priv.pktsize && + (packet[_packetByteCount - 1] & 0x80)) { + priv.PSMOUSE_BAD_DATA = true; + _ringBuffer.advanceHead(priv.pktsize); + return kPS2IR_packetReady; + } + + /* alps_is_valid_package_v7 */ + if (priv.proto_version == ALPS_PROTO_V7 && + (((_packetByteCount == 3) && ((packet[2] & 0x40) != 0x40)) || + ((_packetByteCount == 4) && ((packet[3] & 0x48) != 0x48)) || + ((_packetByteCount == 6) && ((packet[5] & 0x40) != 0x0)))) { + priv.PSMOUSE_BAD_DATA = true; + _ringBuffer.advanceHead(priv.pktsize); + return kPS2IR_packetReady; + } + + /* alps_is_valid_package_ss4_v2 */ + if (priv.proto_version == ALPS_PROTO_V8 && + ((_packetByteCount == 4 && ((packet[3] & 0x08) != 0x08)) || + (_packetByteCount == 6 && ((packet[5] & 0x10) != 0x0)))) { + priv.PSMOUSE_BAD_DATA = true; + _ringBuffer.advanceHead(priv.pktsize); + return kPS2IR_packetReady; + } + + packet[_packetByteCount++] = data; + if (_packetByteCount == priv.pktsize) + { + _ringBuffer.advanceHead(priv.pktsize); + return kPS2IR_packetReady; + } + return kPS2IR_packetBuffering; +} + +void ALPS::packetReady() { + // empty the ring buffer, dispatching each packet... + while (_ringBuffer.count() >= priv.pktsize) { + UInt8 *packet = _ringBuffer.tail(); + if (priv.PSMOUSE_BAD_DATA == false) { + (this->*process_packet)(packet); + } else { + IOLog("ALPS: an invalid or bare packet has been dropped...\n"); + /* Might need to perform a full HW reset here if we keep receiving bad packets (consecutively) */ + } + _packetByteCount = 0; + _ringBuffer.advanceTail(priv.pktsize); + } +} + +bool ALPS::alps_command_mode_send_nibble(int nibble) { + SInt32 command; + // The largest amount of requests we will have is 2 right now + // 1 for the initial command, and 1 for sending data OR 1 for receiving data + // If the nibble commands at the top change then this will need to change as + // well. For now we will just validate that the request will not overload + // this object. + TPS2Request<2> request; + int cmdCount = 0, send = 0, receive = 0, i; + + if (nibble > 0xf) { + IOLog("%s::alps_command_mode_send_nibble ERROR: nibble value is greater than 0xf, command may fail\n", getName()); + } + + request.commands[cmdCount].command = kPS2C_SendMouseCommandAndCompareAck; + command = priv.nibble_commands[nibble].command; + request.commands[cmdCount++].inOrOut = command & 0xff; + + send = (command >> 12 & 0xf); + receive = (command >> 8 & 0xf); + + if ((send > 1) || ((send + receive + 1) > 2)) { + return false; + } + + if (send > 0) { + request.commands[cmdCount].command = kPS2C_SendMouseCommandAndCompareAck; + request.commands[cmdCount++].inOrOut = priv.nibble_commands[nibble].data; + } + + for (i = 0; i < receive; i++) { + request.commands[cmdCount].command = kPS2C_ReadDataPort; + request.commands[cmdCount++].inOrOut = 0; + } + + request.commandsCount = cmdCount; + assert(request.commandsCount <= countof(request.commands)); + + _device->submitRequestAndBlock(&request); + + return request.commandsCount == cmdCount; +} + +bool ALPS::alps_command_mode_set_addr(int addr) { + + TPS2Request<1> request; + int i, nibble; + + // DEBUG_LOG("command mode set addr with addr command: 0x%02x\n", priv.addr_command); + request.commands[0].command = kPS2C_SendMouseCommandAndCompareAck; + request.commands[0].inOrOut = priv.addr_command; + request.commandsCount = 1; + _device->submitRequestAndBlock(&request); + + if (request.commandsCount != 1) { + return false; + } + + for (i = 12; i >= 0; i -= 4) { + nibble = (addr >> i) & 0xf; + if (!alps_command_mode_send_nibble(nibble)) { + return false; + } + } + + return true; +} + +int ALPS::alps_command_mode_read_reg(int addr) { + TPS2Request<4> request; + ALPSStatus_t status; + + if (!alps_command_mode_set_addr(addr)) { + DEBUG_LOG("Failed to set addr to read register\n"); + return -1; + } + + request.commands[0].command = kPS2C_SendMouseCommandAndCompareAck; + request.commands[0].inOrOut = kDP_GetMouseInformation; //sync.. + request.commands[1].command = kPS2C_ReadDataPort; + request.commands[1].inOrOut = 0; + request.commands[2].command = kPS2C_ReadDataPort; + request.commands[2].inOrOut = 0; + request.commands[3].command = kPS2C_ReadDataPort; + request.commands[3].inOrOut = 0; + request.commandsCount = 4; + assert(request.commandsCount <= countof(request.commands)); + _device->submitRequestAndBlock(&request); + + if (request.commandsCount != 4) { + return -1; + } + + status.bytes[0] = request.commands[1].inOrOut; + status.bytes[1] = request.commands[2].inOrOut; + status.bytes[2] = request.commands[3].inOrOut; + + //IOLog("ALPS read reg result: { 0x%02x, 0x%02x, 0x%02x }\n", status.bytes[0], status.bytes[1], status.bytes[2]); + + /* The address being read is returned in the first 2 bytes + * of the result. Check that the address matches the expected + * address. + */ + if (addr != ((status.bytes[0] << 8) | status.bytes[1])) { + DEBUG_LOG("ALPS ERROR: read wrong registry value, expected: %x\n", addr); + return -1; + } + + return status.bytes[2]; +} + +bool ALPS::alps_command_mode_write_reg(int addr, UInt8 value) { + + if (!alps_command_mode_set_addr(addr)) { + return false; + } + + return alps_command_mode_write_reg(value); +} + +bool ALPS::alps_command_mode_write_reg(UInt8 value) { + if (!alps_command_mode_send_nibble((value >> 4) & 0xf)) { + return false; + } + if (!alps_command_mode_send_nibble(value & 0xf)) { + return false; + } + + return true; +} + +bool ALPS::alps_rpt_cmd(SInt32 init_command, SInt32 init_arg, SInt32 repeated_command, ALPSStatus_t *report) { + TPS2Request<9> request; + int byte0, cmd; + cmd = byte0 = 0; + + if (init_command) { + request.commands[cmd].command = kPS2C_SendMouseCommandAndCompareAck; + request.commands[cmd++].inOrOut = kDP_SetMouseResolution; + request.commands[cmd].command = kPS2C_SendMouseCommandAndCompareAck; + request.commands[cmd++].inOrOut = init_arg; + } + + + // 3X run command + request.commands[cmd].command = kPS2C_SendMouseCommandAndCompareAck; + request.commands[cmd++].inOrOut = repeated_command; + request.commands[cmd].command = kPS2C_SendMouseCommandAndCompareAck; + request.commands[cmd++].inOrOut = repeated_command; + request.commands[cmd].command = kPS2C_SendMouseCommandAndCompareAck; + request.commands[cmd++].inOrOut = repeated_command; + + // Get info/result + request.commands[cmd].command = kPS2C_SendMouseCommandAndCompareAck; + request.commands[cmd++].inOrOut = kDP_GetMouseInformation; + byte0 = cmd; + request.commands[cmd].command = kPS2C_ReadDataPort; + request.commands[cmd++].inOrOut = 0; + request.commands[cmd].command = kPS2C_ReadDataPort; + request.commands[cmd++].inOrOut = 0; + request.commands[cmd].command = kPS2C_ReadDataPort; + request.commands[cmd++].inOrOut = 0; + request.commandsCount = cmd; + assert(request.commandsCount <= countof(request.commands)); + _device->submitRequestAndBlock(&request); + + report->bytes[0] = request.commands[byte0].inOrOut; + report->bytes[1] = request.commands[byte0+1].inOrOut; + report->bytes[2] = request.commands[byte0+2].inOrOut; + + DEBUG_LOG("%02x report: [0x%02x 0x%02x 0x%02x]\n", + repeated_command, + report->bytes[0], + report->bytes[1], + report->bytes[2]); + + return request.commandsCount == cmd; +} + +bool ALPS::alps_enter_command_mode() { + DEBUG_LOG("enter command mode\n"); + TPS2Request<4> request; + ALPSStatus_t status; + + if (!alps_rpt_cmd(NULL, NULL, kDP_MouseResetWrap, &status)) { + IOLog("ALPS: Failed to enter command mode!\n"); + return false; + } + return true; +} + +bool ALPS::alps_exit_command_mode() { + DEBUG_LOG("exit command mode\n"); + TPS2Request<1> request; + + request.commands[0].command = kPS2C_SendMouseCommandAndCompareAck; + request.commands[0].inOrOut = kDP_SetMouseStreamMode; + request.commandsCount = 1; + assert(request.commandsCount <= countof(request.commands)); + _device->submitRequestAndBlock(&request); + + return true; +} + +bool ALPS::alps_passthrough_mode_v2(bool enable) { + int cmd = enable ? kDP_SetMouseScaling2To1 : kDP_SetMouseScaling1To1; + TPS2Request<4> request; + + request.commands[0].command = kPS2C_SendMouseCommandAndCompareAck; + request.commands[0].inOrOut = cmd; + request.commands[1].command = kPS2C_SendMouseCommandAndCompareAck; + request.commands[1].inOrOut = cmd; + request.commands[2].command = kPS2C_SendMouseCommandAndCompareAck; + request.commands[2].inOrOut = cmd; + request.commands[3].command = kPS2C_SendMouseCommandAndCompareAck; + request.commands[3].inOrOut = kDP_SetDefaultsAndDisable; + request.commandsCount = 4; + assert(request.commandsCount <= countof(request.commands)); + _device->submitRequestAndBlock(&request); + + return request.commandsCount == 4; +} + +bool ALPS::alps_absolute_mode_v1_v2() { + + ps2_command_short(kDP_SetDefaultsAndDisable); + ps2_command_short(kDP_SetDefaultsAndDisable); + ps2_command_short(kDP_SetDefaultsAndDisable); + ps2_command_short(kDP_SetDefaultsAndDisable); + ps2_command_short(kDP_Enable); + + /* + * Switch mouse to poll (remote) mode so motion data will not + * get in our way + */ + ps2_command_short(kDP_MouseSetPoll); + + return true; +} + +int ALPS::alps_monitor_mode_send_word(int word) +{ + int i, nibble; + + for (i = 0; i <= 8; i += 4) { + nibble = (word >> i) & 0xf; + alps_command_mode_send_nibble(nibble); + } + + return 0; +} + +int ALPS::alps_monitor_mode_write_reg(int addr, int value) +{ + ps2_command_short(kDP_Enable); + alps_monitor_mode_send_word(0x0A0); + alps_monitor_mode_send_word(addr); + alps_monitor_mode_send_word(value); + ps2_command_short(kDP_SetDefaultsAndDisable); + + return 0; +} + +int ALPS::alps_monitor_mode(bool enable) +{ + TPS2Request<4> request; + int cmd = 0; + + if (enable) { + + ps2_command_short(kDP_MouseResetWrap); + request.commands[cmd].command = kPS2C_SendMouseCommandAndCompareAck; + request.commands[cmd++].inOrOut = kDP_GetMouseInformation; + request.commands[cmd].command = kPS2C_ReadDataPort; + request.commands[cmd++].inOrOut = 0; + request.commands[cmd].command = kPS2C_ReadDataPort; + request.commands[cmd++].inOrOut = 0; + request.commands[cmd].command = kPS2C_ReadDataPort; + request.commands[cmd++].inOrOut = 0; + request.commandsCount = cmd; + assert(request.commandsCount <= countof(request.commands)); + _device->submitRequestAndBlock(&request); + + ps2_command_short(kDP_SetDefaultsAndDisable); + ps2_command_short(kDP_SetDefaultsAndDisable); + ps2_command_short(kDP_SetMouseScaling2To1); + ps2_command_short(kDP_SetMouseScaling1To1); + ps2_command_short(kDP_SetMouseScaling2To1); + + /* Get Info */ + request.commands[cmd].command = kPS2C_SendMouseCommandAndCompareAck; + request.commands[cmd++].inOrOut = kDP_GetMouseInformation; + request.commands[cmd].command = kPS2C_ReadDataPort; + request.commands[cmd++].inOrOut = 0; + request.commands[cmd].command = kPS2C_ReadDataPort; + request.commands[cmd++].inOrOut = 0; + request.commands[cmd].command = kPS2C_ReadDataPort; + request.commands[cmd++].inOrOut = 0; + request.commandsCount = cmd; + assert(request.commandsCount <= countof(request.commands)); + _device->submitRequestAndBlock(&request); + } else { + ps2_command_short(kDP_MouseResetWrap); + } + + return 0; +} + +void ALPS::alps_absolute_mode_v6() +{ + // enter monitor mode, to write the register / + alps_monitor_mode(true); + alps_monitor_mode_write_reg(0x000, 0x181); + alps_monitor_mode(false); +} + +bool ALPS::alps_get_status(ALPSStatus_t *status) { + return alps_rpt_cmd(NULL, NULL, kDP_SetDefaultsAndDisable, status); +} + +/* + * Turn touchpad tapping on or off. The sequences are: + * 0xE9 0xF5 0xF5 0xF3 0x0A to enable, + * 0xE9 0xF5 0xF5 0xE8 0x00 to disable. + * My guess that 0xE9 (GetInfo) is here as a sync point. + * For models that also have stickpointer (DualPoints) its tapping + * is controlled separately (0xE6 0xE6 0xE6 0xF3 0x14|0x0A) but + * we don't fiddle with it. + */ +bool ALPS::alps_tap_mode(bool enable) { + int cmd = enable ? kDP_SetMouseSampleRate : kDP_SetMouseResolution; + UInt8 tapArg = enable ? 0x0A : 0x00; + TPS2Request<8> request; + ALPSStatus_t result; + + request.commands[0].command = kPS2C_SendMouseCommandAndCompareAck; + request.commands[0].inOrOut = kDP_GetMouseInformation; + request.commands[1].command = kPS2C_ReadDataPort; + request.commands[1].inOrOut = 0; + request.commands[2].command = kPS2C_ReadDataPort; + request.commands[2].inOrOut = 0; + request.commands[3].command = kPS2C_ReadDataPort; + request.commands[3].inOrOut = 0; + request.commands[4].command = kPS2C_SendMouseCommandAndCompareAck; + request.commands[4].inOrOut = kDP_SetDefaultsAndDisable; + request.commands[5].command = kPS2C_SendMouseCommandAndCompareAck; + request.commands[5].inOrOut = kDP_SetDefaultsAndDisable; + request.commands[6].command = kPS2C_SendMouseCommandAndCompareAck; + request.commands[6].inOrOut = cmd; + request.commands[7].command = kPS2C_SendMouseCommandAndCompareAck; + request.commands[7].inOrOut = tapArg; + request.commandsCount = 8; + _device->submitRequestAndBlock(&request); + + if (request.commandsCount != 8) { + DEBUG_LOG("Enabling tap mode failed before getStatus call, command count=%d\n", + request.commandsCount); + return false; + } + + return alps_get_status(&result); +} + + +bool ALPS::alps_hw_init_v1_v2() { + TPS2Request<1> request; + + if (priv.flags & ALPS_PASS) { + if (!alps_passthrough_mode_v2(true)) { + return false; + } + } + + if (!alps_tap_mode(true)) { + return false; + } + + if (!alps_absolute_mode_v1_v2()) { + IOLog("ERROR: Failed to enable absolute mode\n"); + return false; + } + + if (priv.flags & ALPS_PASS) { + if (!alps_passthrough_mode_v2(false)) { + return false; + } + } + + /* ALPS needs stream mode, otherwise it won't report any data */ + ps2_command_short(kDP_SetMouseStreamMode); + + return true; +} + +bool ALPS::alps_hw_init_v6() +{ + //TODO: V6 is not yet fully implemented. + //unsigned char param[2] = {0xC8, 0x14}; + + /* Enter passthrough mode to let trackpoint enter 6byte raw mode */ + alps_passthrough_mode_v2(true); + + ps2_command_short(kDP_SetMouseScaling1To1); + ps2_command_short(kDP_SetMouseScaling1To1); + ps2_command_short(kDP_SetMouseScaling1To1); + ps2_command(0xC8, kDP_SetMouseSampleRate); + ps2_command(0x14, kDP_SetMouseSampleRate); + + alps_passthrough_mode_v2(false); + + alps_absolute_mode_v6(); + + return true; +} + +bool ALPS::alps_passthrough_mode_v3(int regBase, bool enable) { + int regVal; + bool ret = false; + + DEBUG_LOG("passthrough mode enable=%d\n", enable); + + if (!alps_enter_command_mode()) { + IOLog("ALPS: Failed to enter command mode while enabling passthrough mode\n"); + return false; + } + + regVal = alps_command_mode_read_reg(regBase + 0x0008); + if (regVal == -1) { + IOLog("ALPS: Failed to read register while setting up passthrough mode\n"); + goto error; + } + + if (enable) { + regVal |= 0x01; + } else { + regVal &= ~0x01; + } + + ret = alps_command_mode_write_reg(regVal); + +error: + if (!alps_exit_command_mode()) { + IOLog("ALPS: failed to exit command mode while enabling passthrough mode v3\n"); + return false; + } + + return ret; +} + +bool ALPS::alps_absolute_mode_v3() { + + int regVal; + + regVal = alps_command_mode_read_reg(0x0004); + if (regVal == -1) { + return false; + } + + regVal |= 0x06; + if (!alps_command_mode_write_reg(regVal)) { + return false; + } + + return true; +} + +IOReturn ALPS::alps_probe_trackstick_v3_v7(int regBase) { + int ret = kIOReturnIOError, regVal; + + if (!alps_enter_command_mode()) { + goto error; + } + + regVal = alps_command_mode_read_reg(regBase + 0x08); + + if (regVal == -1) { + goto error; + } + + /* bit 7: trackstick is present */ + ret = regVal & 0x80 ? 0 : kIOReturnNoDevice; + +error: + alps_exit_command_mode(); + return ret; +} + +IOReturn ALPS::alps_setup_trackstick_v3(int regBase) { + IOReturn ret = 0; + ALPSStatus_t report; + TPS2Request<3> request; + + if (!alps_passthrough_mode_v3(regBase, true)) { + return kIOReturnIOError; + } + + /* + * E7 report for the trackstick + * + * There have been reports of failures to seem to trace back + * to the above trackstick check failing. When these occur + * this E7 report fails, so when that happens we continue + * with the assumption that there isn't a trackstick after + * all. + */ + if (!alps_rpt_cmd(NULL, NULL, kDP_SetMouseScaling2To1, &report)) { + IOLog("ALPS: trackstick E7 report failed\n"); + ret = kIOReturnNoDevice; + } else { + /* + * Not sure what this does, but it is absolutely + * essential. Without it, the touchpad does not + * work at all and the trackstick just emits normal + * PS/2 packets. + */ + request.commands[0].command = kPS2C_SendMouseCommandAndCompareAck; + request.commands[0].inOrOut = kDP_SetMouseScaling1To1; + request.commands[1].command = kPS2C_SendMouseCommandAndCompareAck; + request.commands[1].inOrOut = kDP_SetMouseScaling1To1; + request.commands[2].command = kPS2C_SendMouseCommandAndCompareAck; + request.commands[2].inOrOut = kDP_SetMouseScaling1To1; + request.commandsCount = 3; + assert(request.commandsCount <= countof(request.commands)); + _device->submitRequestAndBlock(&request); + if (request.commandsCount != 3) { + IOLog("ALPS: error sending magic E6 scaling sequence\n"); + ret = kIOReturnIOError; + goto error; + } + if (!(alps_command_mode_send_nibble(0x9) && alps_command_mode_send_nibble(0x4))) { + IOLog("ALPS: error sending magic E6 nibble sequence\n"); + ret = kIOReturnIOError; + goto error; + } + DEBUG_LOG("Sent magic E6 sequence\n"); + + /* + * This ensures the trackstick packets are in the format + * supported by this driver. If bit 1 isn't set the packet + * format is different. + */ + if (!(alps_enter_command_mode() && + alps_command_mode_write_reg(regBase + 0x0008, 0x82) && + alps_exit_command_mode())) { + ret = -kIOReturnIOError; + //goto error; + } + } +error: + if (!alps_passthrough_mode_v3(regBase, false)) { + ret = kIOReturnIOError; + } + + return ret; +} + +bool ALPS::alps_hw_init_v3() { + int regVal; + + if ((priv.flags & ALPS_DUALPOINT) && + alps_setup_trackstick_v3(ALPS_REG_BASE_PINNACLE) == kIOReturnIOError) + goto error; + + if (!(alps_enter_command_mode() && + alps_absolute_mode_v3())) { + IOLog("ALPS: Failed to enter absolute mode\n"); + goto error; + } + + regVal = alps_command_mode_read_reg(0x0006); + if (regVal == -1) + goto error; + if (!alps_command_mode_write_reg(regVal | 0x01)) + goto error; + + regVal = alps_command_mode_read_reg(0x0007); + if (regVal == -1) + goto error; + if (!alps_command_mode_write_reg(regVal | 0x01)) + goto error; + + if (alps_command_mode_read_reg(0x0144) == -1) + goto error; + if (!alps_command_mode_write_reg(0x04)) + goto error; + + if (alps_command_mode_read_reg(0x0159) == -1) + goto error; + if (!alps_command_mode_write_reg(0x03)) + goto error; + + if (alps_command_mode_read_reg(0x0163) == -1) + goto error; + if (!alps_command_mode_write_reg(0x0163, 0x03)) + goto error; + + if (alps_command_mode_read_reg(0x0162) == -1) + goto error; + if (!alps_command_mode_write_reg(0x0162, 0x04)) + goto error; + + alps_exit_command_mode(); + + /* Set rate and enable data reporting */ + ps2_command(0x28, kDP_SetMouseSampleRate); + ps2_command_short(kDP_Enable); + + return true; + +error: + alps_exit_command_mode(); + return false; +} + +bool ALPS::alps_get_v3_v7_resolution(int reg_pitch) +{ + int reg, x_pitch, y_pitch, x_electrode, y_electrode, x_phys, y_phys; + + reg = alps_command_mode_read_reg(reg_pitch); + if (reg < 0) + return reg; + + x_pitch = (char)(reg << 4) >> 4; /* sign extend lower 4 bits */ + x_pitch = 50 + 2 * x_pitch; /* In 0.1 mm units */ + + y_pitch = (char)reg >> 4; /* sign extend upper 4 bits */ + y_pitch = 36 + 2 * y_pitch; /* In 0.1 mm units */ + + reg = alps_command_mode_read_reg(reg_pitch + 1); + if (reg < 0) + return reg; + + x_electrode = (char)(reg << 4) >> 4; /* sign extend lower 4 bits */ + x_electrode = 17 + x_electrode; + + y_electrode = (char)reg >> 4; /* sign extend upper 4 bits */ + y_electrode = 13 + y_electrode; + + x_phys = x_pitch * (x_electrode - 1); /* In 0.1 mm units */ + y_phys = y_pitch * (y_electrode - 1); /* In 0.1 mm units */ + + priv.x_res = priv.x_max * 10 / x_phys; /* units / mm */ + priv.y_res = priv.y_max * 10 / y_phys; /* units / mm */ + + /*IOLog("pitch %dx%d num-electrodes %dx%d physical size %dx%d mm res %dx%d\n", + x_pitch, y_pitch, x_electrode, y_electrode, + x_phys / 10, y_phys / 10, priv.x_res, priv.y_res);*/ + + return true; +} + +bool ALPS::alps_hw_init_rushmore_v3() { + int regVal; + + if (priv.flags & ALPS_DUALPOINT) { + regVal = alps_setup_trackstick_v3(ALPS_REG_BASE_RUSHMORE); + if (regVal == kIOReturnIOError) { + goto error; + } + } + + if (!alps_enter_command_mode() || + alps_command_mode_read_reg(0xc2d9) == -1 || + !alps_command_mode_write_reg(0xc2cb, 0x00)) { + goto error; + } + + regVal = alps_command_mode_read_reg(0xc2c6); + if (regVal == -1) + goto error; + if (!alps_command_mode_write_reg(regVal & 0xfd)) + goto error; + + if (!alps_command_mode_write_reg(0xc2c9, 0x64)) + goto error; + + /* enter absolute mode */ + regVal = alps_command_mode_read_reg(0xc2c4); + if (regVal == -1) + goto error; + if (!alps_command_mode_write_reg(regVal | 0x02)) + goto error; + + alps_exit_command_mode(); + + /* Enable data reporting */ + ps2_command_short(kDP_Enable); + + return true; + +error: + alps_exit_command_mode(); + return false; +} + +/* + * Used during both passthrough mode initialization and touchpad enablement + */ + + +/* Must be in command mode when calling this function */ +bool ALPS::alps_absolute_mode_v4() { + int regVal; + + regVal = alps_command_mode_read_reg(0x0004); + if (regVal == -1) { + return false; + } + + regVal |= 0x02; + if (!alps_command_mode_write_reg(regVal)) { + return false; + } + + return true; +} + +bool ALPS::alps_hw_init_v4() { + + if (!alps_enter_command_mode()) { + goto error; + } + + if (!alps_absolute_mode_v4()) { + IOLog("ALPS: Failed to enter absolute mode\n"); + goto error; + } + + if (!alps_command_mode_write_reg(0x0007, 0x8c)) { + goto error; + } + + if (!alps_command_mode_write_reg(0x0149, 0x03)) { + goto error; + } + + if (!alps_command_mode_write_reg(0x0160, 0x03)) { + goto error; + } + + if (!alps_command_mode_write_reg(0x017f, 0x15)) { + goto error; + } + + if (!alps_command_mode_write_reg(0x0151, 0x01)) { + goto error; + } + + if (!alps_command_mode_write_reg(0x0168, 0x03)) { + goto error; + } + + if (!alps_command_mode_write_reg(0x014a, 0x03)) { + goto error; + } + + if (!alps_command_mode_write_reg(0x0161, 0x03)) { + goto error; + } + + alps_exit_command_mode(); + + /* + * This sequence changes the output from a 9-byte to an + * 8-byte format. All the same data seems to be present, + * just in a more compact format. + */ + ps2_command(0xc8, kDP_SetMouseSampleRate); + ps2_command(0x64, kDP_SetMouseSampleRate); + ps2_command(0x50, kDP_SetMouseSampleRate); + ps2_command_short(kDP_GetId); + + /* Set rate and enable data reporting */ + ps2_command(0x64, kDP_SetMouseSampleRate); + ps2_command_short(kDP_Enable); + return true; + +error: + alps_exit_command_mode(); + return false; +} + +void ALPS::alps_get_otp_values_ss4_v2(unsigned char index) +{ + int cmd = 0; + TPS2Request<4> request; + + switch (index) { + case 0: + ps2_command_short(kDP_SetMouseStreamMode); + ps2_command_short(kDP_SetMouseStreamMode); + + request.commands[cmd].command = kPS2C_SendMouseCommandAndCompareAck; + request.commands[cmd++].inOrOut = kDP_GetMouseInformation; + request.commands[cmd].command = kPS2C_ReadDataPort; + request.commands[cmd++].inOrOut = 0; + request.commands[cmd].command = kPS2C_ReadDataPort; + request.commands[cmd++].inOrOut = 0; + request.commands[cmd].command = kPS2C_ReadDataPort; + request.commands[cmd++].inOrOut = 0; + request.commandsCount = cmd; + assert(request.commandsCount <= countof(request.commands)); + _device->submitRequestAndBlock(&request); + + break; + + case 1: + ps2_command_short(kDP_MouseSetPoll); + ps2_command_short(kDP_MouseSetPoll); + + request.commands[cmd].command = kPS2C_SendMouseCommandAndCompareAck; + request.commands[cmd++].inOrOut = kDP_GetMouseInformation; + request.commands[cmd].command = kPS2C_ReadDataPort; + request.commands[cmd++].inOrOut = 0; + request.commands[cmd].command = kPS2C_ReadDataPort; + request.commands[cmd++].inOrOut = 0; + request.commands[cmd].command = kPS2C_ReadDataPort; + request.commands[cmd++].inOrOut = 0; + request.commandsCount = cmd; + assert(request.commandsCount <= countof(request.commands)); + _device->submitRequestAndBlock(&request); + + break; + } +} + +void ALPS::alps_set_defaults_ss4_v2(struct alps_data *priv) +{ + alps_get_otp_values_ss4_v2(0); + alps_get_otp_values_ss4_v2(1); + +} + +int ALPS::alps_dolphin_get_device_area(struct alps_data *priv) +{ + int num_x_electrode, num_y_electrode; + TPS2Request<4> request; + int cmd = 0; + ALPSStatus_t status; + + alps_enter_command_mode(); + + ps2_command_short(kDP_MouseResetWrap); + ps2_command_short(kDP_MouseSetPoll); + ps2_command_short(kDP_MouseSetPoll); + ps2_command(0x0a, kDP_SetMouseSampleRate); + ps2_command(0x0a, kDP_SetMouseSampleRate); + + request.commands[cmd].command = kPS2C_SendMouseCommandAndCompareAck; + request.commands[cmd++].inOrOut = kDP_GetMouseInformation; + request.commands[cmd].command = kPS2C_ReadDataPort; + request.commands[cmd++].inOrOut = 0; + request.commands[cmd].command = kPS2C_ReadDataPort; + request.commands[cmd++].inOrOut = 0; + request.commands[cmd].command = kPS2C_ReadDataPort; + request.commands[cmd++].inOrOut = 0; + request.commandsCount = cmd; + assert(request.commandsCount <= countof(request.commands)); + _device->submitRequestAndBlock(&request); + + /* results */ + status.bytes[0] = request.commands[1].inOrOut; + status.bytes[1] = request.commands[2].inOrOut; + status.bytes[2] = request.commands[3].inOrOut; + + num_x_electrode = DOLPHIN_PROFILE_XOFFSET + (status.bytes[2] & 0x0F); + num_y_electrode = DOLPHIN_PROFILE_YOFFSET + ((status.bytes[2] >> 4) & 0x0F); + priv->x_bits = num_x_electrode; + priv->y_bits = num_y_electrode; + priv->x_max = (num_x_electrode - 1) * DOLPHIN_COUNT_PER_ELECTRODE; + priv->y_max = (num_y_electrode - 1) * DOLPHIN_COUNT_PER_ELECTRODE; + + alps_exit_command_mode(); + + return 0; +} + +bool ALPS::alps_hw_init_dolphin_v1() { + + ps2_command_short(kDP_SetMouseStreamMode); + ps2_command(0x64, kDP_SetMouseSampleRate); + ps2_command(0x28, kDP_SetMouseSampleRate); + ps2_command_short(kDP_Enable); + + return true; +} + +bool ALPS::alps_hw_init_v7(){ + int reg_val; + + if (!alps_enter_command_mode()) + goto error; + + if (alps_command_mode_read_reg(0xc2d9) == -1) + goto error; + + if (!alps_get_v3_v7_resolution(0xc397)) + goto error; + + if (!alps_command_mode_write_reg(0xc2c9, 0x64)) + goto error; + + reg_val = alps_command_mode_read_reg(0xc2c4); + if (reg_val == -1) + goto error; + + if (!alps_command_mode_write_reg(reg_val | 0x02)) + goto error; + + alps_exit_command_mode(); + + ps2_command(0x28, kDP_SetMouseSampleRate); + ps2_command_short(kDP_Enable); + + return true; + +error: + alps_exit_command_mode(); + return false; +} + +bool ALPS::alps_hw_init_ss4_v2() +{ + /* enter absolute mode */ + ps2_command_short(kDP_SetMouseStreamMode); + ps2_command_short(kDP_SetMouseStreamMode); + ps2_command(0x64, kDP_SetMouseSampleRate); + ps2_command(0x28, kDP_SetMouseSampleRate); + + /* T.B.D. Decread noise packet number, delete in the future */ + alps_exit_command_mode(); + alps_enter_command_mode(); + alps_command_mode_write_reg(0x001D, 0x20); + alps_exit_command_mode(); + + /* final init */ + ps2_command_short(kDP_Enable); + + return true; + +} + +bool ALPS::ps2_command(unsigned char value, UInt8 command) +{ + TPS2Request<2> request; + int cmdCount = 0; + + request.commands[cmdCount].command = kPS2C_SendMouseCommandAndCompareAck; + request.commands[cmdCount++].inOrOut = command; + request.commands[cmdCount].command = kPS2C_SendMouseCommandAndCompareAck; + request.commands[cmdCount++].inOrOut = value; + request.commandsCount = cmdCount; + assert(request.commandsCount <= countof(request.commands)); + _device->submitRequestAndBlock(&request); + + if (request.commandsCount != 2) { + return false; + } + //return request.commandsCount = cmdCount; +} + +bool ALPS::ps2_command_short(UInt8 command) +{ + TPS2Request<1> request; + int cmdCount = 0; + + request.commands[cmdCount].command = kPS2C_SendMouseCommandAndCompareAck; + request.commands[cmdCount++].inOrOut = command; + request.commandsCount = cmdCount; + assert(request.commandsCount <= countof(request.commands)); + _device->submitRequestAndBlock(&request); + + if (request.commandsCount != 1) { + return false; + } + //return request.commandsCount = cmdCount; +} + +void ALPS::set_protocol() { + priv.byte0 = 0x8f; + priv.mask0 = 0x8f; + priv.flags = ALPS_DUALPOINT; + + priv.x_max = 2000; + priv.y_max = 1400; + priv.x_bits = 15; + priv.y_bits = 11; + + switch (priv.proto_version) { + case ALPS_PROTO_V1: + case ALPS_PROTO_V2: + hw_init = &ALPS::alps_hw_init_v1_v2; + process_packet = &ALPS::alps_process_packet_v1_v2; + priv.x_max = 1023; + priv.y_max = 767; + // set_abs_params = alps_set_abs_params_st; + break; + + case ALPS_PROTO_V3: + hw_init = &ALPS::alps_hw_init_v3; + process_packet = &ALPS::alps_process_packet_v3; + // set_abs_params = alps_set_abs_params_mt; + decode_fields = &ALPS::alps_decode_pinnacle; + priv.nibble_commands = alps_v3_nibble_commands; + priv.addr_command = kDP_MouseResetWrap; + + if (alps_probe_trackstick_v3_v7(ALPS_REG_BASE_PINNACLE)) { + priv.flags &= ~ALPS_DUALPOINT; + } else { + IOLog("ALPS: TrackStick detected...\n"); + } + + break; + + case ALPS_PROTO_V3_RUSHMORE: + hw_init = &ALPS::alps_hw_init_rushmore_v3; + process_packet = &ALPS::alps_process_packet_v3; + // set_abs_params = alps_set_abs_params_mt; + decode_fields = &ALPS::alps_decode_rushmore; + priv.nibble_commands = alps_v3_nibble_commands; + priv.addr_command = kDP_MouseResetWrap; + priv.x_bits = 16; + priv.y_bits = 12; + + if (alps_probe_trackstick_v3_v7(ALPS_REG_BASE_RUSHMORE)) { + priv.flags &= ~ALPS_DUALPOINT; + } else { + IOLog("ALPS: TrackStick detected...\n"); + } + + break; + + case ALPS_PROTO_V4: + hw_init = &ALPS::alps_hw_init_v4; + process_packet = &ALPS::alps_process_packet_v4; + // set_abs_params = alps_set_abs_params_mt; + priv.nibble_commands = alps_v4_nibble_commands; + priv.addr_command = kDP_SetDefaultsAndDisable; + break; + + case ALPS_PROTO_V5: + hw_init = &ALPS::alps_hw_init_dolphin_v1; + process_packet = &ALPS::alps_process_touchpad_packet_v3_v5; + decode_fields = &ALPS::alps_decode_dolphin; + // set_abs_params = alps_set_abs_params_mt; + priv.nibble_commands = alps_v3_nibble_commands; + priv.addr_command = kDP_MouseResetWrap; + priv.byte0 = 0xc8; + priv.mask0 = 0xc8; + priv.flags = 0; + //priv.x_max = 1360; + //priv.y_max = 660; + priv.x_bits = 23; + priv.y_bits = 12; + + alps_dolphin_get_device_area(&priv); + + break; + + case ALPS_PROTO_V6: + //hw_init = &ApplePS2ALPSGlidePoint::hwInitV6_version2; + //process_packet = &ApplePS2ALPSGlidePoint::processPacketV6; + priv.nibble_commands = alps_v6_nibble_commands; + priv.addr_command = kDP_MouseResetWrap; + priv.byte0 = 0xc8; + priv.mask0 = 0xc8; + priv.flags = 0; + priv.x_max = 2047; + priv.y_max = 1535; + //decode_fields = &ApplePS2ALPSGlidePoint::decodePacketV6; + break; + + case ALPS_PROTO_V7: + hw_init = &ALPS::alps_hw_init_v7; + process_packet = &ALPS::alps_process_packet_v7; + decode_fields = &ALPS::alps_decode_packet_v7; + priv.nibble_commands = alps_v3_nibble_commands; + priv.addr_command = kDP_MouseResetWrap; + priv.byte0 = 0x48; + priv.mask0 = 0x48; + + priv.x_max = 0xfff; + priv.y_max = 0x7ff; + + if (priv.fw_ver[1] != 0xba){ + priv.flags |= ALPS_BUTTONPAD; + IOLog("ALPS: ButtonPad Detected...\n"); + } + + if (alps_probe_trackstick_v3_v7(ALPS_REG_BASE_V7)){ + priv.flags &= ~ALPS_DUALPOINT; + } else { + IOLog("ALPS: TrackStick detected... (WARNING: V7 TrackStick disabled)\n"); + } + + break; + + case ALPS_PROTO_V8: + hw_init = &ALPS::alps_hw_init_ss4_v2; + process_packet = &ALPS::alps_process_packet_ss4_v2; + decode_fields = &ALPS::alps_decode_ss4_v2; + priv.nibble_commands = alps_v3_nibble_commands; + priv.addr_command = kDP_MouseResetWrap; + priv.byte0 = 0x18; + priv.mask0 = 0x18; + priv.flags = 0; + + alps_set_defaults_ss4_v2(&priv); + + //TODO: V8: add detection of tarckstick using the "alps_set_defaults_ss4_v2(&priv)" funtcion + if (priv.fw_ver[1] == 0x1) { + // buttons and trackpad + priv.x_max = 8160; + priv.y_max = 4080; + priv.flags |= ALPS_DUALPOINT | + ALPS_DUALPOINT_WITH_PRESSURE; + IOLog("ALPS: TrackStick detected... (WARNING: V8 TrackStick disabled)\n"); + } else { + // buttonless + priv.x_max = 8176; + priv.y_max = 4088; + priv.flags |= ALPS_BUTTONPAD; + IOLog("ALPS: ButtonPad Detected...\n"); + } + break; + } +} + +bool ALPS::matchTable(ALPSStatus_t *e7, ALPSStatus_t *ec) { + const struct alps_model_info *model; + int i; + + for (i = 0; i < ARRAY_SIZE(alps_model_data); i++) { + model = &alps_model_data[i]; + + if (!memcmp(e7->bytes, model->signature, sizeof(model->signature)) && + (!model->command_mode_resp || + model->command_mode_resp == ec->bytes[2])) { + + priv.proto_version = model->proto_version; + + // log model version: + if (priv.proto_version == ALPS_PROTO_V1) { + IOLog("ALPS: Found an ALPS V1 TouchPad\n"); + } else if (priv.proto_version == ALPS_PROTO_V2) { + IOLog("ALPS: Found an ALPS V2 TouchPad\n"); + } else if (priv.proto_version == ALPS_PROTO_V4) { + IOLog("ALPS: Found an ALPS V4 TouchPad\n"); + }else if (priv.proto_version == ALPS_PROTO_V6) { + IOLog("ALPS: Found an ALPS V6 TouchPad\n"); + } + + set_protocol(); + + priv.flags = model->flags; + priv.byte0 = model->byte0; + priv.mask0 = model->mask0; + + return true; + } + } + + return false; +} + +IOReturn ALPS::identify() { + ALPSStatus_t e6, e7, ec; + + /* + * First try "E6 report". + * ALPS should return 0,0,10 or 0,0,100 if no buttons are pressed. + * The bits 0-2 of the first byte will be 1s if some buttons are + * pressed. + */ + + if (!alps_rpt_cmd(kDP_SetMouseResolution, NULL, kDP_SetMouseScaling1To1, &e6)) { + IOLog("ALPS: identify: not an ALPS device. Error getting E6 report\n"); + //return kIOReturnIOError; + } + + if ((e6.bytes[0] & 0xf8) != 0 || e6.bytes[1] != 0 || (e6.bytes[2] != 10 && e6.bytes[2] != 100)) { + IOLog("ALPS: identify: not an ALPS device. Invalid E6 report\n"); + //return kIOReturnInvalid; + } + + /* + * Now get the "E7" and "EC" reports. These will uniquely identify + * most ALPS touchpads. + */ + if (!(alps_rpt_cmd(kDP_SetMouseResolution, NULL, kDP_SetMouseScaling2To1, &e7) && + alps_rpt_cmd(kDP_SetMouseResolution, NULL, kDP_MouseResetWrap, &ec) && + alps_exit_command_mode())) { + IOLog("ALPS: identify: not an ALPS device. Error getting E7/EC report\n"); + return kIOReturnIOError; + } + + if (matchTable(&e7, &ec)) { + return 0; + + } else if (e7.bytes[0] == 0x73 && e7.bytes[1] == 0x03 && e7.bytes[2] == 0x50 && + ec.bytes[0] == 0x73 && (ec.bytes[1] == 0x01 || ec.bytes[1] == 0x02)) { + priv.proto_version = ALPS_PROTO_V5; + IOLog("ALPS: Found a V5 Dolphin TouchPad with ID: E7=0x%02x 0x%02x 0x%02x, EC=0x%02x 0x%02x 0x%02x\n", e7.bytes[0], e7.bytes[1], e7.bytes[2], ec.bytes[0], ec.bytes[1], ec.bytes[2]); + + } else if (ec.bytes[0] == 0x88 && + ((ec.bytes[1] & 0xf0) == 0xb0 || (ec.bytes[1] & 0xf0) == 0xc0)) { + priv.proto_version = ALPS_PROTO_V7; + IOLog("ALPS: Found a V7 TouchPad with ID: E7=0x%02x 0x%02x 0x%02x, EC=0x%02x 0x%02x 0x%02x\n", e7.bytes[0], e7.bytes[1], e7.bytes[2], ec.bytes[0], ec.bytes[1], ec.bytes[2]); + + } else if (ec.bytes[0] == 0x88 && ec.bytes[1] == 0x08) { + priv.proto_version = ALPS_PROTO_V3_RUSHMORE; + IOLog("ALPS: Found a V3 Rushmore TouchPad with ID: E7=0x%02x 0x%02x 0x%02x, EC=0x%02x 0x%02x 0x%02x\n", e7.bytes[0], e7.bytes[1], e7.bytes[2], ec.bytes[0], ec.bytes[1], ec.bytes[2]); + + } else if (ec.bytes[0] == 0x88 && ec.bytes[1] == 0x07 && + ec.bytes[2] >= 0x90 && ec.bytes[2] <= 0x9d) { + priv.proto_version = ALPS_PROTO_V3; + IOLog("ALPS: Found a V3 Pinnacle TouchPad with ID: E7=0x%02x 0x%02x 0x%02x, EC=0x%02x 0x%02x 0x%02x\n", e7.bytes[0], e7.bytes[1], e7.bytes[2], ec.bytes[0], ec.bytes[1], ec.bytes[2]); + + } else if (e7.bytes[0] == 0x73 && e7.bytes[1] == 0x03 && + e7.bytes[2] == 0x14 && ec.bytes[1] == 0x02) { + priv.proto_version = ALPS_PROTO_V8; + IOLog("ALPS: Found a V8 TouchPad with ID: E7=0x%02x 0x%02x 0x%02x, EC=0x%02x 0x%02x 0x%02x\n", e7.bytes[0], e7.bytes[1], e7.bytes[2], ec.bytes[0], ec.bytes[1], ec.bytes[2]); + + + } else if (e7.bytes[0] == 0x73 && e7.bytes[1] == 0x03 && + e7.bytes[2] == 0x28 && ec.bytes[1] == 0x01) { + priv.proto_version = ALPS_PROTO_V8; + IOLog("ALPS: Found a V8 Flare TouchPad with ID: E7=0x%02x 0x%02x 0x%02x, EC=0x%02x 0x%02x 0x%02x\n", e7.bytes[0], e7.bytes[1], e7.bytes[2], ec.bytes[0], ec.bytes[1], ec.bytes[2]); + + + } else { + IOLog("ALPS DRIVER: TouchPad didn't match any known IDs: E7=0x%02x 0x%02x 0x%02x, EC=0x%02x 0x%02x 0x%02x ... driver will now exit\n", + e7.bytes[0], e7.bytes[1], e7.bytes[2], ec.bytes[0], ec.bytes[1], ec.bytes[2]); + return kIOReturnInvalid; + } + + /* Save the Firmware version */ + memcpy(priv.fw_ver, ec.bytes, 3); + set_protocol(); + return 0; +} + +/* ============================================================================================== */ +/* ===========================||\\PROCESS AND DISPATCH TO macOS//||============================== */ +/* ============================================================================================== */ + +void ALPS::dispatchEventsWithInfo(int xraw, int yraw, int z, int fingers, UInt32 buttonsraw) { + uint64_t now_abs; + clock_get_uptime(&now_abs); + uint64_t now_ns; + absolutetime_to_nanoseconds(now_abs, &now_ns); + + // scale x & y to the axis which has the most resolution + if (xupmm < yupmm) { + xraw = xraw * yupmm / xupmm; + } else if (xupmm > yupmm) { + yraw = yraw * xupmm / yupmm; + } + + /* Dr Hurt: Scale all touchpads' axes to 6000 to be able to the same divisors for all models */ + xraw *= (6000 / ((priv.x_max + priv.y_max)/2)); + yraw *= (6000 / ((priv.x_max + priv.y_max)/2)); + + int x = xraw; + int y = yraw; + + fingers = z > z_finger ? fingers : 0; + + // allow middle click to be simulated the other two physical buttons + UInt32 buttons = buttonsraw; + lastbuttons = buttons; + + // allow middle button to be simulated with two buttons down + if (!clickpadtype || fingers == 3) { + buttons = middleButton(buttons, now_abs, fingers == 3 ? fromPassthru : fromTrackpad); + DEBUG_LOG("New buttons value after check for middle click: %d\n", buttons); + } + + // recalc middle buttons if finger is going down + if (0 == last_fingers && fingers > 0) { + buttons = middleButton(buttonsraw | passbuttons, now_abs, fromCancel); + } + + if (last_fingers > 0 && fingers > 0 && last_fingers != fingers) { + // ignore deltas for a while after finger change + ignoredeltas = ignoredeltasstart; + } + + if (last_fingers != fingers) { + DEBUG_LOG("Finger change, reset averages\n"); + // reset averages after finger change + x_undo.reset(); + y_undo.reset(); + x_avg.reset(); + y_avg.reset(); + } + + // unsmooth input (probably just for testing) + // by default the trackpad itself does a simple decaying average (1/2 each) + // we can undo it here + if (unsmoothinput) { + x = x_undo.filter(x); + y = y_undo.filter(y); + } + + // smooth input by unweighted average + if (smoothinput) { + x = x_avg.filter(x); + y = y_avg.filter(y); + } + + if (ignoredeltas) { + DEBUG_LOG("ALPS: Still ignoring deltas. Value=%d\n", ignoredeltas); + lastx = x; + lasty = y; + if (--ignoredeltas == 0) { + x_undo.reset(); + y_undo.reset(); + x_avg.reset(); + y_avg.reset(); + } + } + + // deal with "OutsidezoneNoAction When Typing" + if (outzone_wt && z > z_finger && now_ns - keytime < maxaftertyping && + (x < zonel || x > zoner || y < zoneb || y > zonet)) { + DEBUG_LOG("Ignore touch input after typing\n"); + // touch input was shortly after typing and outside the "zone" + // ignore it... + return; + } + + // if trackpad input is supposed to be ignored, then don't do anything + if (ignoreall) { + DEBUG_LOG("ignoreall is set, returning\n"); + return; + } + +#ifdef DEBUG_VERBOSE + int tm1 = touchmode; +#endif + if (z < z_finger && isTouchMode()) { + // Finger has been lifted + DEBUG_LOG("finger lifted after touch\n"); + xrest = yrest = scrollrest = 0; + inSwipeLeft = inSwipeRight = inSwipeUp = inSwipeDown = 0; + inSwipe4Left = inSwipe4Right = inSwipe4Up = inSwipe4Down = 0; + xmoved = ymoved = 0; + untouchtime = now_ns; + + // check for scroll momentum start + if ((MODE_MTOUCH == touchmode || MODE_VSCROLL == touchmode) && momentumscroll && momentumscrolltimer) { + // releasing when we were in touchmode -- check for momentum scroll + if (dy_history.count() > momentumscrollsamplesmin && + (momentumscrollinterval = time_history.newest() - time_history.oldest())) { + momentumscrollsum = dy_history.sum(); + momentumscrollcurrent = momentumscrolltimer * momentumscrollsum; + momentumscrollrest1 = 0; + momentumscrollrest2 = 0; + setTimerTimeout(scrollTimer, momentumscrolltimer); + } + } + time_history.reset(); + dy_history.reset(); + + if (now_ns - touchtime < maxtaptime && clicking) { + switch (touchmode) { + case MODE_DRAG: + if (!immediateclick) { + buttons &= ~0x7; + dispatchRelativePointerEventX(0, 0, buttons | 0x1, now_abs); + dispatchRelativePointerEventX(0, 0, buttons, now_abs); + } + if (wastriple && rtap) { + buttons |= !swapdoubletriple ? 0x4 : 0x02; + } else if (wasdouble && rtap) { + buttons |= !swapdoubletriple ? 0x2 : 0x04; + } else { + buttons |= 0x1; + } + touchmode = MODE_NOTOUCH; + break; + + case MODE_DRAGLOCK: + touchmode = MODE_NOTOUCH; + break; + + default: //dispatch taps + if (wastriple && rtap) + { + buttons |= !swapdoubletriple ? 0x4 : 0x02; + touchmode=MODE_NOTOUCH; + } + else if (wasdouble && rtap) + { + buttons |= !swapdoubletriple ? 0x2 : 0x04; + touchmode=MODE_NOTOUCH; + } + else + { + buttons |= 0x1; + touchmode=dragging ? MODE_PREDRAG : MODE_NOTOUCH; + } + break; + } + } + else { + if ((touchmode==MODE_DRAG || touchmode==MODE_DRAGLOCK) + && (draglock || draglocktemp || (dragTimer && dragexitdelay))) + { + touchmode=MODE_DRAGNOTOUCH; + if (!draglock && !draglocktemp) + { + cancelTimer(dragTimer); + setTimerTimeout(dragTimer, dragexitdelay); + } + } else { + touchmode = MODE_NOTOUCH; + draglocktemp = 0; + } + } + wasdouble = false; + wastriple = false; + } + + // cancel pre-drag mode if second tap takes too long + if (touchmode == MODE_PREDRAG && now_ns - untouchtime >= maxdragtime) { + DEBUG_LOG("cancel pre-drag since second tap took too long\n"); + touchmode = MODE_NOTOUCH; + } + + // Note: This test should probably be done somewhere else, especially if to + // implement more gestures in the future, because this information we are + // erasing here (time of touch) might be useful for certain gestures... + + // cancel tap if touch point moves too far + if (isTouchMode() && isFingerTouch(z)) { + int dx = xraw > touchx ? xraw - touchx : touchx - xraw; + int dy = yraw > touchy ? touchy - yraw : yraw - touchy; + if (!wasdouble && !wastriple && (dx > tapthreshx || dy > tapthreshy)) { + touchtime = 0; + } + else if (dx > dblthreshx || dy > dblthreshy) { + touchtime = 0; + } + } + +#ifdef DEBUG_VERBOSE + int tm2 = touchmode; +#endif + int dx = 0, dy = 0; + + switch (touchmode) { + case MODE_DRAG: + case MODE_DRAGLOCK: + if (MODE_DRAGLOCK == touchmode || (!immediateclick || now_ns - touchtime > maxdbltaptime)) { + buttons |= 0x1; + } + // fall through + case MODE_MOVE: + if (last_fingers == fingers && z<=zlimit) + { + if (now_ns - touchtime > 100000000) { + dx = x-lastx+xrest; + dy = lasty-y+yrest; + xrest = dx % divisorx; + yrest = dy % divisory; + if (abs(dx) > bogusdxthresh || abs(dy) > bogusdythresh) + dx = dy = xrest = yrest = 0; + } + } + break; + + case MODE_MTOUCH: + switch (fingers) { + case 1: + // transition from multitouch to single touch + // continue moving with the primary finger + if (!wsticky) + { + dy_history.reset(); + time_history.reset(); + touchmode=MODE_MOVE; + break; + } + case 2: // two finger + if (last_fingers != fingers) { + break; + } + if (palm && z > zlimit) { + break; + } + if (palm_wt && now_ns - keytime < maxaftertyping) { + break; + } + dy = (wvdivisor) ? (y-lasty+yrest) : 0; + dx = (whdivisor&&hscroll) ? (x-lastx+xrest) : 0; + yrest = (wvdivisor) ? dy % wvdivisor : 0; + xrest = (whdivisor&&hscroll) ? dx % whdivisor : 0; + // check for stopping or changing direction + if ((dy < 0) != (dy_history.newest() < 0) || dy == 0) { + // stopped or changed direction, clear history + dy_history.reset(); + time_history.reset(); + } + // put movement and time in history for later + dy_history.filter(dy); + time_history.filter(now_ns); + //REVIEW: filter out small movements (Mavericks issue) + if (abs(dx) < scrolldxthresh) + { + xrest = dx; + dx = 0; + } + if (abs(dy) < scrolldythresh) + { + yrest = dy; + dy = 0; + } + if (0 != dy || 0 != dx) + { + + dispatchScrollWheelEventX(wvdivisor ? dy / wvdivisor : 0, (whdivisor && hscroll) ? -dx / whdivisor : 0, 0, now_abs); + dx = dy = 0; + } + break; + + case 3: // three finger + if (last_fingers != fingers) { + break; + } + + if (threefingerhorizswipe || threefingervertswipe) { + // Now calculate total movement since 3 fingers down (add to total) + xmoved += lastx-x; + ymoved += y-lasty; + + // dispatching 3 finger movement + if (ymoved > swipedy && !inSwipeUp && !inSwipe4Up && threefingervertswipe) { + inSwipeUp = 1; + inSwipeDown = 0; + ymoved = 0; + _device->dispatchKeyboardMessage(kPS2M_swipeUp, &now_abs); + break; + } + if (ymoved < -swipedy && !inSwipeDown && !inSwipe4Down && threefingervertswipe) { + inSwipeDown = 1; + inSwipeUp = 0; + ymoved = 0; + _device->dispatchKeyboardMessage(kPS2M_swipeDown, &now_abs); + break; + } + if (xmoved < -swipedx && !inSwipeRight && !inSwipe4Right && threefingerhorizswipe) { + inSwipeRight = 1; + inSwipeLeft = 0; + xmoved = 0; + _device->dispatchKeyboardMessage(kPS2M_swipeRight, &now_abs); + break; + } + if (xmoved > swipedx && !inSwipeLeft && !inSwipe4Left && threefingerhorizswipe) { + inSwipeLeft = 1; + inSwipeRight = 0; + xmoved = 0; + _device->dispatchKeyboardMessage(kPS2M_swipeLeft, &now_abs); + break; + } + } + break; + + case 4: // four fingers + if (last_fingers != fingers) { + break; + } + + // Now calculate total movement since 4 fingers down (add to total) + xmoved += lastx-x; + ymoved += y-lasty; + + // dispatching 4 finger movement + if (ymoved > swipedy && !inSwipe4Up) { + inSwipe4Up = 1; inSwipeUp = 0; + inSwipe4Down = 0; + ymoved = 0; + _device->dispatchKeyboardMessage(kPS2M_swipe4Up, &now_abs); + break; + } + if (ymoved < -swipedy && !inSwipe4Down) { + inSwipe4Down = 1; inSwipeDown = 0; + inSwipe4Up = 0; + ymoved = 0; + _device->dispatchKeyboardMessage(kPS2M_swipe4Down, &now_abs); + break; + } + if (xmoved < -swipedx && !inSwipe4Right) { + inSwipe4Right = 1; inSwipeRight = 0; + inSwipe4Left = 0; + xmoved = 0; + _device->dispatchKeyboardMessage(kPS2M_swipe4Right, &now_abs); + break; + } + if (xmoved > swipedx && !inSwipe4Left) { + inSwipe4Left = 1; inSwipeLeft = 0; + inSwipe4Right = 0; + xmoved = 0; + _device->dispatchKeyboardMessage(kPS2M_swipe4Left, &now_abs); + break; + } + } + break; + + case MODE_VSCROLL: + if (!vsticky && (x < redge || fingers > 1 || z > zlimit)) { + touchmode = MODE_NOTOUCH; + break; + } + if (palm_wt && now_ns - keytime < maxaftertyping) { + break; + } + dy = y-lasty+scrollrest; + scrollrest = dy % vscrolldivisor; + //REVIEW: filter out small movements (Mavericks issue) + if (abs(dy) < scrolldythresh) + { + scrollrest = dy; + dy = 0; + } + if ((dy < 0) != (dy_history.newest() < 0) || dy == 0) { + // stopped or changed direction, clear history + dy_history.reset(); + time_history.reset(); + } + // put movement and time in history for later + dy_history.filter(dy); + time_history.filter(now_ns); + if (dy) + { + dispatchScrollWheelEventX(dy / vscrolldivisor, 0, 0, now_abs); + dy = 0; + } + break; + + case MODE_HSCROLL: + if (!hsticky && (y > bedge || fingers > 1 || z > zlimit)) { + touchmode = MODE_NOTOUCH; + break; + } + if (palm_wt && now_ns - keytime < maxaftertyping) { + break; + } + dx = lastx-x+scrollrest; + scrollrest = dx % hscrolldivisor; + //REVIEW: filter out small movements (Mavericks issue) + if (abs(dx) < scrolldxthresh) + { + scrollrest = dx; + dx = 0; + } + if (dx) + { + dispatchScrollWheelEventX(0, dx / hscrolldivisor, 0, now_abs); + dx = 0; + } + break; + + case MODE_CSCROLL: + if (palm_wt && now_ns - keytime < maxaftertyping) { + break; + } + + if (y < centery) { + dx = x - lastx; + } + else { + dx = lastx - x; + } + + if (x < centerx) { + dx += lasty - y; + } + else { + dx += y - lasty; + dx += scrollrest; + scrollrest = dx % cscrolldivisor; + } + //REVIEW: filter out small movements (Mavericks issue) + if (abs(dx) < scrolldxthresh) + { + scrollrest = dx; + dx = 0; + } + if (dx) + { + dispatchScrollWheelEventX(dx / cscrolldivisor, 0, 0, now_abs); + dx = 0; + } + break; + + case MODE_DRAGNOTOUCH: + buttons |= 0x1; + // fall through + case MODE_PREDRAG: + if (!immediateclick && (!palm_wt || now_ns - keytime >= maxaftertyping)) { + buttons |= 0x1; + } + case MODE_NOTOUCH: + break; + + default: + ; // nothing + } + + // capture time of tap, and watch for double/triple tap + if (isFingerTouch(z)) { + // taps don't count if too close to typing or if currently in momentum scroll + if ((!palm_wt || now_ns - keytime >= maxaftertyping) && !momentumscrollcurrent) { + if (!isTouchMode()) { + touchtime = now_ns; + touchx = x; + touchy = y; + } + if (fingers == 2) { + wasdouble = true; + } else if (fingers == 3) { + wastriple = true; + } + } + // any touch cancels momentum scroll + momentumscrollcurrent = 0; + } + // switch modes, depending on input + if (touchmode == MODE_PREDRAG && isFingerTouch(z)) { + touchmode = MODE_DRAG; + draglocktemp = _modifierdown & draglocktempmask; + } + if (touchmode == MODE_DRAGNOTOUCH && isFingerTouch(z)) { + if (dragTimer) + cancelTimer(dragTimer); + touchmode=MODE_DRAGLOCK; + } + if (MODE_MTOUCH != touchmode && fingers > 1 && isFingerTouch(z)) { + touchmode = MODE_MTOUCH; + } + + if (scroll && cscrolldivisor) { + if (touchmode == MODE_NOTOUCH && z > z_finger && y > tedge && (ctrigger == 1 || ctrigger == 9)) + touchmode = MODE_CSCROLL; + if (touchmode == MODE_NOTOUCH && z > z_finger && y > tedge && x > redge && (ctrigger == 2)) + touchmode = MODE_CSCROLL; + if (touchmode == MODE_NOTOUCH && z > z_finger && x > redge && (ctrigger == 3 || ctrigger == 9)) + touchmode = MODE_CSCROLL; + if (touchmode == MODE_NOTOUCH && z > z_finger && x > redge && y < bedge && (ctrigger == 4)) + touchmode = MODE_CSCROLL; + if (touchmode == MODE_NOTOUCH && z > z_finger && y < bedge && (ctrigger == 5 || ctrigger == 9)) + touchmode = MODE_CSCROLL; + if (touchmode == MODE_NOTOUCH && z > z_finger && y < bedge && x < ledge && (ctrigger == 6)) + touchmode = MODE_CSCROLL; + if (touchmode == MODE_NOTOUCH && z > z_finger && x < ledge && (ctrigger == 7 || ctrigger == 9)) + touchmode = MODE_CSCROLL; + if (touchmode == MODE_NOTOUCH && z > z_finger && x < ledge && y > tedge && (ctrigger == 8)) + touchmode = MODE_CSCROLL; + } + if ((MODE_NOTOUCH == touchmode || (MODE_HSCROLL == touchmode && y >= bedge)) && + z > z_finger && x > redge && vscrolldivisor && scroll) { + touchmode = MODE_VSCROLL; + scrollrest = 0; + } + if ((MODE_NOTOUCH == touchmode || (MODE_VSCROLL == touchmode && x <= redge)) && + z > z_finger && y < bedge && hscrolldivisor && scroll) { + touchmode = MODE_HSCROLL; + scrollrest = 0; + } + if (touchmode == MODE_NOTOUCH && z > z_finger) { + touchmode = MODE_MOVE; + } + + // dispatch dx/dy and current button status + dispatchRelativePointerEventX(dx / divisorx, dy / divisory, buttons, now_abs); + + // always save last seen position for calculating deltas later + lastx = x; + lasty = y; + //b4last = last_fingers; + last_fingers = fingers; + +#ifdef DEBUG_VERBOSE + DEBUG_LOG("ps2: dx=%d, dy=%d (%d,%d) z=%d mode=(%d,%d,%d) buttons=%d wasdouble=%d wastriple=%d\n", dx, dy, x, y, z, tm1, tm2, touchmode, buttons, wasdouble, wastriple); +#endif +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +void ALPS:: +dispatchRelativePointerEventWithPacket(UInt8 *packet, + UInt32 packetSize) { + // + // Process the three byte relative format packet that was retrieved from the + // trackpad. The format of the bytes is as follows: + // + // 7 6 5 4 3 2 1 0 + // ----------------------- + // YO XO YS XS 1 M R L + // X7 X6 X5 X4 X3 X3 X1 X0 (X delta) + // Y7 Y6 Y5 Y4 Y3 Y2 Y1 Y0 (Y delta) + // + + UInt32 buttons = 0; + SInt32 dx, dy; + + if ((packet[0] & 0x1)) buttons |= 0x1; // left button (bit 0 in packet) + if ((packet[0] & 0x2)) buttons |= 0x2; // right button (bit 1 in packet) + if ((packet[0] & 0x4)) buttons |= 0x4; // middle button (bit 2 in packet) + + dx = packet[1]; + if (dx) { + dx = packet[1] - ((packet[0] << 4) & 0x100); + } + + dy = packet[2]; + if (dy) { + dy = ((packet[0] << 3) & 0x100) - packet[2]; + } + + uint64_t now_abs; + clock_get_uptime(&now_abs); + IOLog("ALPS: Dispatch relative PS2 packet: dx=%d, dy=%d, buttons=%d\n", dx, dy, buttons); + dispatchRelativePointerEventX(dx, dy, buttons, now_abs); +} diff --git a/VoodooPS2Trackpad/alps.h b/VoodooPS2Trackpad/alps.h new file mode 100644 index 0000000..a35a4b8 --- /dev/null +++ b/VoodooPS2Trackpad/alps.h @@ -0,0 +1,459 @@ +/* + * Copyright (c) 2002 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * The contents of this file constitute Original Code as defined in and + * are subject to the Apple Public Source License Version 1.2 (the + * "License"). You may not use this file except in compliance with the + * License. Please obtain a copy of the License at + * http://www.apple.com/publicsource and read it before using this file. + * + * This Original Code and all software distributed under the License are + * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the + * License for the specific language governing rights and limitations + * under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +#include "VoodooPS2TouchPadBase.h" + +#define ALPS_PROTO_V1 0x100 +#define ALPS_PROTO_V2 0x200 +#define ALPS_PROTO_V3 0x300 +#define ALPS_PROTO_V3_RUSHMORE 0x310 +#define ALPS_PROTO_V4 0x400 +#define ALPS_PROTO_V5 0x500 +#define ALPS_PROTO_V6 0x600 +#define ALPS_PROTO_V7 0x700 /* t3btl t4s */ +#define ALPS_PROTO_V8 0x800 /* SS4btl SS4s */ + +#define MAX_TOUCHES 4 + +#define DOLPHIN_COUNT_PER_ELECTRODE 64 +#define DOLPHIN_PROFILE_XOFFSET 8 /* x-electrode offset */ +#define DOLPHIN_PROFILE_YOFFSET 1 /* y-electrode offset */ + +/* + * enum SS4_PACKET_ID - defines the packet type for V8 + * SS4_PACKET_ID_IDLE: There's no finger and no button activity. + * SS4_PACKET_ID_ONE: There's one finger on touchpad + * or there's button activities. + * SS4_PACKET_ID_TWO: There's two or more fingers on touchpad + * SS4_PACKET_ID_MULTI: There's three or more fingers on touchpad + */ +enum SS4_PACKET_ID { + SS4_PACKET_ID_IDLE = 0, + SS4_PACKET_ID_ONE, + SS4_PACKET_ID_TWO, + SS4_PACKET_ID_MULTI, + SS4_PACKET_ID_STICK, +}; + +#define SS4_COUNT_PER_ELECTRODE 256 +#define SS4_NUMSENSOR_XOFFSET 7 +#define SS4_NUMSENSOR_YOFFSET 7 +#define SS4_MIN_PITCH_MM 50 + +#define SS4_MASK_NORMAL_BUTTONS 0x07 + +#define SS4_1F_X_V2(_b) ((_b[0] & 0x0007) | \ +((_b[1] << 3) & 0x0078) | \ +((_b[1] << 2) & 0x0380) | \ +((_b[2] << 5) & 0x1C00) \ +) + +#define SS4_1F_Y_V2(_b) (((_b[2]) & 0x000F) | \ +((_b[3] >> 2) & 0x0030) | \ +((_b[4] << 6) & 0x03C0) | \ +((_b[4] << 5) & 0x0C00) \ +) + +#define SS4_1F_Z_V2(_b) (((_b[5]) & 0x0F) | \ +((_b[5] >> 1) & 0x70) | \ +((_b[4]) & 0x80) \ +) + +#define SS4_1F_LFB_V2(_b) (((_b[2] >> 4) & 0x01) == 0x01) + +#define SS4_MF_LF_V2(_b, _i) ((_b[1 + (_i) * 3] & 0x0004) == 0x0004) + +#define SS4_BTN_V2(_b) ((_b[0] >> 5) & SS4_MASK_NORMAL_BUTTONS) + +#define SS4_STD_MF_X_V2(_b, _i) (((_b[0 + (_i) * 3] << 5) & 0x00E0) | \ +((_b[1 + _i * 3] << 5) & 0x1F00) \ +) + +#define SS4_STD_MF_Y_V2(_b, _i) (((_b[1 + (_i) * 3] << 3) & 0x0010) | \ +((_b[2 + (_i) * 3] << 5) & 0x01E0) | \ +((_b[2 + (_i) * 3] << 4) & 0x0E00) \ +) + +#define SS4_BTL_MF_X_V2(_b, _i) (SS4_STD_MF_X_V2(_b, _i) | \ +((_b[0 + (_i) * 3] >> 3) & 0x0010) \ +) + +#define SS4_BTL_MF_Y_V2(_b, _i) (SS4_STD_MF_Y_V2(_b, _i) | \ +((_b[0 + (_i) * 3] >> 3) & 0x0008) \ +) + +#define SS4_MF_Z_V2(_b, _i) (((_b[1 + (_i) * 3]) & 0x0001) | \ +((_b[1 + (_i) * 3] >> 1) & 0x0002) \ +) + +#define SS4_IS_MF_CONTINUE(_b) ((_b[2] & 0x10) == 0x10) +#define SS4_IS_5F_DETECTED(_b) ((_b[2] & 0x10) == 0x10) + + +#define SS4_MFPACKET_NO_AX 8160 /* X-Coordinate value */ +#define SS4_MFPACKET_NO_AY 4080 /* Y-Coordinate value */ +#define SS4_MFPACKET_NO_AX_BL 8176 /* Buttonless X-Coordinate value */ +#define SS4_MFPACKET_NO_AY_BL 4088 /* Buttonless Y-Coordinate value */ + +/* + * enum V7_PACKET_ID - defines the packet type for V7 + * V7_PACKET_ID_IDLE: There's no finger and no button activity. + * V7_PACKET_ID_TWO: There's one or two non-resting fingers on touchpad + * or there's button activities. + * V7_PACKET_ID_MULTI: There are at least three non-resting fingers. + * V7_PACKET_ID_NEW: The finger position in slot is not continues from + * previous packet. + */ +enum V7_PACKET_ID { + V7_PACKET_ID_IDLE, + V7_PACKET_ID_TWO, + V7_PACKET_ID_MULTI, + V7_PACKET_ID_NEW, + V7_PACKET_ID_UNKNOWN, +}; + + +/** + * struct alps_model_info - touchpad ID table + * @signature: E7 response string to match. + * @command_mode_resp: For V3/V4 touchpads, the final byte of the EC response + * (aka command mode response) identifies the firmware minor version. This + * can be used to distinguish different hardware models which are not + * uniquely identifiable through their E7 responses. + * @proto_version: Indicates V1/V2/V3/... + * @byte0: Helps figure out whether a position report packet matches the + * known format for this model. The first byte of the report, ANDed with + * mask0, should match byte0. + * @mask0: The mask used to check the first byte of the report. + * @flags: Additional device capabilities (passthrough port, trackstick, etc.). + * + * Many (but not all) ALPS touchpads can be identified by looking at the + * values returned in the "E7 report" and/or the "EC report." This table + * lists a number of such touchpads. + */ +struct alps_model_info { + UInt8 signature[3]; + UInt8 command_mode_resp; + UInt16 proto_version; + UInt8 byte0, mask0; + unsigned int flags; +}; + +/** + * struct alps_nibble_commands - encodings for register accesses + * @command: PS/2 command used for the nibble + * @data: Data supplied as an argument to the PS/2 command, if applicable + * + * The ALPS protocol uses magic sequences to transmit binary data to the + * touchpad, as it is generally not OK to send arbitrary bytes out the + * PS/2 port. Each of the sequences in this table sends one nibble of the + * register address or (write) data. Different versions of the ALPS protocol + * use slightly different encodings. + */ +struct alps_nibble_commands { + SInt32 command; + UInt8 data; +}; + +struct alps_bitmap_point { + int start_bit; + int num_bits; +}; + +struct input_mt_pos { + UInt32 x; + UInt32 y; +}; + +/** + * struct alps_fields - decoded version of the report packet + * @x_map: Bitmap of active X positions for MT. + * @y_map: Bitmap of active Y positions for MT. + * @fingers: Number of fingers for MT. + * @pressure: Pressure. + * @st: position for ST. + * @mt: position for MT. + * @first_mp: Packet is the first of a multi-packet report. + * @is_mp: Packet is part of a multi-packet report. + * @left: Left touchpad button is active. + * @right: Right touchpad button is active. + * @middle: Middle touchpad button is active. + * @ts_left: Left trackstick button is active. + * @ts_right: Right trackstick button is active. + * @ts_middle: Middle trackstick button is active. + */ +struct alps_fields { + UInt32 x_map; + UInt32 y_map; + UInt32 fingers; + + int pressure; + struct input_mt_pos st; + struct input_mt_pos mt[MAX_TOUCHES]; + + UInt32 first_mp:1; + UInt32 is_mp:1; + + UInt32 left:1; + UInt32 right:1; + UInt32 middle:1; + + UInt32 ts_left:1; + UInt32 ts_right:1; + UInt32 ts_middle:1; +}; + +class ALPS; + +/** + * struct alps_data - private data structure for the ALPS driver + * @nibble_commands: Command mapping used for touchpad register accesses. + * @addr_command: Command used to tell the touchpad that a register address + * follows. + * @proto_version: Indicates V1/V2/V3/... + * @byte0: Helps figure out whether a position report packet matches the + * known format for this model. The first byte of the report, ANDed with + * mask0, should match byte0. + * @mask0: The mask used to check the first byte of the report. + * @fw_ver: cached copy of firmware version (EC report) + * @flags: Additional device capabilities (passthrough port, trackstick, etc.). + * @x_max: Largest possible X position value. + * @y_max: Largest possible Y position value. + * @x_bits: Number of X bits in the MT bitmap. + * @y_bits: Number of Y bits in the MT bitmap. + * @prev_fin: Finger bit from previous packet. + * @multi_packet: Multi-packet data in progress. + * @multi_data: Saved multi-packet data. + * @f: Decoded packet data fields. + * @quirks: Bitmap of ALPS_QUIRK_*. + */ +struct alps_data { + /* these are autodetected when the device is identified */ + const struct alps_nibble_commands *nibble_commands; + SInt32 addr_command; + UInt16 proto_version; + UInt8 byte0, mask0; + UInt8 fw_ver[3]; + int flags; + SInt32 x_max; + SInt32 y_max; + SInt32 x_bits; + SInt32 y_bits; + unsigned int x_res; + unsigned int y_res; + + SInt32 prev_fin; + SInt32 multi_packet; + int second_touch; + UInt8 multi_data[6]; + struct alps_fields f; + UInt8 quirks; + bool PSMOUSE_BAD_DATA; + + int pktsize = 6; +}; + +// Pulled out of alps_data, now saved as vars on class +// makes invoking a little easier +typedef bool (ALPS::*hw_init)(); +typedef bool (ALPS::*decode_fields)(struct alps_fields *f, UInt8 *p); +typedef void (ALPS::*process_packet)(UInt8 *packet); +//typedef void (ALPS::*set_abs_params)(); + +#define ALPS_QUIRK_TRACKSTICK_BUTTONS 1 /* trakcstick buttons in trackstick packet */ + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +// ALPS Class Declaration +// + +typedef struct ALPSStatus { + UInt8 bytes[3]; +} ALPSStatus_t; + +#define kPacketLengthSmall 3 +#define kPacketLengthLarge 6 +#define kPacketLengthMax 6 +#define kDP_CommandNibble10 0xf2 +#define BITS_PER_BYTE 8 + +// predeclure stuff +struct alps_data; + +class EXPORT ALPS : public VoodooPS2TouchPadBase { + typedef VoodooPS2TouchPadBase super; + OSDeclareDefaultStructors( ALPS ); + +private: + alps_data priv; + hw_init hw_init; + decode_fields decode_fields; + process_packet process_packet; + // set_abs_params set_abs_params; + +public: + virtual ALPS * probe(IOService *provider, SInt32 *score); + + bool init(OSDictionary * dict); + + void stop(IOService *provider); + +protected: + int _multiPacket; + + UInt8 _multiData[6]; + + IOGBounds _bounds; + + virtual bool deviceSpecificInit(); + + bool resetMouse(); + + void alps_process_packet_v1_v2(UInt8 *packet); + + int alps_process_bitmap(struct alps_data *priv, struct alps_fields *f); + + void alps_process_trackstick_packet_v3(UInt8 * packet); + + bool alps_decode_buttons_v3(struct alps_fields *f, UInt8 *p); + + bool alps_decode_pinnacle(struct alps_fields *f, UInt8 *p); + + bool alps_decode_rushmore(struct alps_fields *f, UInt8 *p); + + bool alps_decode_dolphin(struct alps_fields *f, UInt8 *p); + + void alps_process_touchpad_packet_v3_v5(UInt8 * packet); + + void alps_process_packet_v3(UInt8 *packet); + + void alps_process_packet_v6(UInt8 *packet); + + void alps_process_packet_v4(UInt8 *packet); + + unsigned char alps_get_packet_id_v7(UInt8 *byte); + + void alps_get_finger_coordinate_v7(struct input_mt_pos *mt, UInt8 *pkt, UInt8 pkt_id); + + int alps_get_mt_count(struct input_mt_pos *mt); + + bool alps_decode_packet_v7(struct alps_fields *f, UInt8 *p); + + void alps_process_trackstick_packet_v7(UInt8 *packet); + + void alps_process_touchpad_packet_v7(UInt8 *packet); + + void alps_process_packet_v7(UInt8 *packet); + + unsigned char alps_get_pkt_id_ss4_v2(UInt8 *byte); + + bool alps_decode_ss4_v2(struct alps_fields *f, UInt8 *p); + + void alps_process_packet_ss4_v2(UInt8 *packet); + + void dispatchEventsWithInfo(int xraw, int yraw, int z, int fingers, UInt32 buttonsraw); + + virtual void dispatchRelativePointerEventWithPacket(UInt8 *packet, UInt32 packetSize); + + void setTouchPadEnable(bool enable); + + PS2InterruptResult interruptOccurred(UInt8 data); + + void packetReady(); + + bool alps_command_mode_send_nibble(int value); + + bool alps_command_mode_set_addr(int addr); + + int alps_command_mode_read_reg(int addr); + + bool alps_command_mode_write_reg(int addr, UInt8 value); + + bool alps_command_mode_write_reg(UInt8 value); + + bool alps_rpt_cmd(SInt32 init_command, SInt32 init_arg, SInt32 repeated_command, ALPSStatus_t *report); + + bool alps_enter_command_mode(); + + bool alps_exit_command_mode(); + + bool alps_passthrough_mode_v2(bool enable); + + bool alps_absolute_mode_v1_v2(); + + int alps_monitor_mode_send_word(int word); + + int alps_monitor_mode_write_reg(int addr, int value); + + int alps_monitor_mode(bool enable); + + void alps_absolute_mode_v6(); + + bool alps_get_status(ALPSStatus_t *status); + + bool alps_tap_mode(bool enable); + + bool alps_hw_init_v1_v2(); + + bool alps_hw_init_v6(); + + bool alps_passthrough_mode_v3(int regBase, bool enable); + + bool alps_absolute_mode_v3(); + + IOReturn alps_probe_trackstick_v3_v7(int regBase); + + IOReturn alps_setup_trackstick_v3(int regBase); + + bool alps_hw_init_v3(); + + bool alps_get_v3_v7_resolution(int reg_pitch); + + bool alps_hw_init_rushmore_v3(); + + bool alps_absolute_mode_v4(); + + bool alps_hw_init_v4(); + + void alps_get_otp_values_ss4_v2(unsigned char index); + + void alps_set_defaults_ss4_v2(struct alps_data *priv); + + int alps_dolphin_get_device_area(struct alps_data *priv); + + bool alps_hw_init_dolphin_v1(); + + bool alps_hw_init_v7(); + + bool alps_hw_init_ss4_v2(); + + bool ps2_command_short(UInt8 command); + + bool ps2_command(unsigned char value, UInt8 command); + + void set_protocol(); + + bool matchTable(ALPSStatus_t *e7, ALPSStatus_t *ec); + + IOReturn identify(); + + void restart(); +}; diff --git a/VoodooPS2Trackpad/en.lproj/InfoPlist.strings b/VoodooPS2Trackpad/en.lproj/InfoPlist.strings new file mode 100644 index 0000000..b92732c --- /dev/null +++ b/VoodooPS2Trackpad/en.lproj/InfoPlist.strings @@ -0,0 +1 @@ +/* Localized versions of Info.plist keys */ diff --git a/makefile b/makefile new file mode 100644 index 0000000..47c07c1 --- /dev/null +++ b/makefile @@ -0,0 +1,101 @@ +# really just some handy scripts... + +DIST=RehabMan-Voodoo +INSTDIR=/System/Library/Extensions +KEXT=VoodooPS2Controller.kext + +ifeq ($(findstring 32,$(BITS)),32) +OPTIONS:=$(OPTIONS) -arch i386 +endif + +ifeq ($(findstring 64,$(BITS)),64) +OPTIONS:=$(OPTIONS) -arch x86_64 +endif + +.PHONY: all +all: + xcodebuild build $(OPTIONS) -scheme All -configuration Debug + xcodebuild build $(OPTIONS) -scheme All -configuration Release + +.PHONY: clean +clean: + xcodebuild clean $(OPTIONS) -scheme All -configuration Debug + xcodebuild clean $(OPTIONS) -scheme All -configuration Release + +.PHONY: update_kernelcache +update_kernelcache: + sudo touch /System/Library/Extensions + sudo kextcache -update-volume / + +.PHONY: rehabman_special_settings +rehabman_special_settings: + sudo /usr/libexec/PlistBuddy -c "Set ':IOKitPersonalities:Synaptics TouchPad:Platform Profile:Default:DragLockTempMask' 262148" $(INSTDIR)/$(KEXT)/Contents/PlugIns/VoodooPS2Trackpad.kext/Contents/Info.plist + #sudo /usr/libexec/PlistBuddy -c "Set ':IOKitPersonalities:Synaptics TouchPad:Platform Profile:HPQOEM:ProBook:FingerZ' 47" $(INSTDIR)/$(KEXT)/Contents/PlugIns/VoodooPS2Trackpad.kext/Contents/Info.plist + +.PHONY: install_debug +install_debug: + sudo rm -Rf $(INSTDIR)/$(KEXT) + sudo cp -R ./Build/Products/Debug/$(KEXT) $(INSTDIR) + if [ "`which tag`" != "" ]; then sudo tag -a Purple $(INSTDIR)/$(KEXT); fi + make rehabman_special_settings + sudo cp ./VoodooPS2Daemon/org.rehabman.voodoo.driver.Daemon.plist /Library/LaunchDaemons + sudo cp ./Build/Products/Debug/VoodooPS2Daemon /usr/bin + if [ "`which tag`" != "" ]; then sudo tag -a Purple /usr/bin/VoodooPS2Daemon; fi + make update_kernelcache + +.PHONY: install +install: install_kext install_daemon + +.PHONY: install_kext +install_kext: + sudo rm -Rf $(INSTDIR)/$(KEXT) + sudo cp -R ./Build/Products/Release/$(KEXT) $(INSTDIR) + if [ "`which tag`" != "" ]; then sudo tag -a Blue $(INSTDIR)/$(KEXT); fi + make rehabman_special_settings + make update_kernelcache + +.PHONY: install_mouse +install_mouse: + sudo rm -Rf $(INSTDIR)/$(KEXT) + sudo cp -R ./Build/Products/Release/$(KEXT) $(INSTDIR) + if [ "`which tag`" != "" ]; then sudo tag -a Blue $(INSTDIR)/$(KEXT); fi + sudo rm -R $(INSTDIR)/$(KEXT)/Contents/PlugIns/VoodooPS2Trackpad.kext + sudo /usr/libexec/PlistBuddy -c "Set ':IOKitPersonalities:ApplePS2Mouse:Platform Profile:HPQOEM:ProBook:DisableDevice' No" $(INSTDIR)/$(KEXT)/Contents/PlugIns/VoodooPS2Mouse.kext/Contents/Info.plist + make update_kernelcache + +.PHONY: install_mouse_debug +install_mouse_debug: + sudo rm -Rf $(INSTDIR)/$(KEXT) + sudo cp -R ./Build/Products/Debug/$(KEXT) $(INSTDIR) + if [ "`which tag`" != "" ]; then sudo tag -a Purple $(INSTDIR)/$(KEXT); fi + sudo rm -R $(INSTDIR)/$(KEXT)/Contents/PlugIns/VoodooPS2Trackpad.kext + sudo /usr/libexec/PlistBuddy -c "Set ':IOKitPersonalities:ApplePS2Mouse:Platform Profile:HPQOEM:ProBook:DisableDevice' No" $(INSTDIR)/$(KEXT)/Contents/PlugIns/VoodooPS2Mouse.kext/Contents/Info.plist + make update_kernelcache + +.PHONY: install_daemon +install_daemon: + sudo cp ./VoodooPS2Daemon/org.rehabman.voodoo.driver.Daemon.plist /Library/LaunchDaemons + sudo cp ./Build/Products/Release/VoodooPS2Daemon /usr/bin + if [ "`which tag`" != "" ]; then sudo tag -a Blue /usr/bin/VoodooPS2Daemon; fi + +install.sh: makefile + make -n install >install.sh + chmod +x install.sh + +.PHONY: distribute +distribute: + if [ -e ./Distribute ]; then rm -r ./Distribute; fi + mkdir ./Distribute + cp -R ./Build/Products/ ./Distribute + find ./Distribute -path *.DS_Store -delete + find ./Distribute -path *.dSYM -exec echo rm -r {} \; >/tmp/org.voodoo.rm.dsym.sh + chmod +x /tmp/org.voodoo.rm.dsym.sh + /tmp/org.voodoo.rm.dsym.sh + rm /tmp/org.voodoo.rm.dsym.sh + cp ./VoodooPS2Daemon/org.rehabman.voodoo.driver.Daemon.plist ./Distribute/ + rm -r ./Distribute/Debug/VoodooPS2synapticsPane.prefPane + rm -r ./Distribute/Release/VoodooPS2synapticsPane.prefPane + rm ./Distribute/Debug/synapticsconfigload + rm ./Distribute/Release/synapticsconfigload + ditto -c -k --sequesterRsrc --zlibCompressionLevel 9 ./Distribute ./Archive.zip + mv ./Archive.zip ./Distribute/`date +$(DIST)-%Y-%m%d.zip` diff --git a/new_kext.cpp b/new_kext.cpp new file mode 100644 index 0000000..ff02643 --- /dev/null +++ b/new_kext.cpp @@ -0,0 +1,75 @@ +// +// new_kext.cpp +// +// Created by RehabMan on 2/3/13. +// Copyright (c) 2013 rehabman. All rights reserved. +// +// Full complement of operator new/delete for use within kext environment. +// + +#include "new_kext.h" + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +// +// Note: Originally these helper functions were exported as C++ functions +// instead of extern "C", and they had much longer names. +// +// But that causes problems for Snow Leopard (crash using Kernel Cache at startup). +// Not sure if it is related to the C++ names, or just the long names, but +// this does work with the shorter extern "C" names, even on Snow Leopard. +// +// Also realize that the calls to IOMallocAligned/IOFreeAligned are mapped to +// IOMalloc/IOFree because there appears to be a bug in the aligned variants. +// See new_kext.h for the macros... +// +// Note: For now we are not using this code, as it is easier to just use the +// built-in operator new/delete. I'm keeping it here, just in case it becomes +// useful in the future. +// + +extern "C" +{ + +void* _opnew(size_t size) +{ + size_t* p = (size_t*)IOMallocAligned(sizeof(size_t) + size, sizeof(void*)); + if (p) + *p++ = size; + return p; +} + +void _opdel(void* p) +{ + assert(p); + if (p) + { + size_t* t = (size_t*)p-1; + IOFreeAligned(t, *t); + } +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +void* _opnewa(size_t size) +{ + size_t* p = (size_t*)IOMallocAligned(sizeof(size_t) + size, sizeof(void*)); + if (p) + *p++ = size; + return p; +} + +void _opdela(void* p) +{ + assert(p); + if (p) + { + size_t* t = (size_t*)p-1; + IOFreeAligned(t, *t); + } +} + +} // extern "C" + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + diff --git a/new_kext.h b/new_kext.h new file mode 100644 index 0000000..12ab1a7 --- /dev/null +++ b/new_kext.h @@ -0,0 +1,37 @@ +// +// new_kext.h +// +// Created by RehabMan on 2/3/13. +// Copyright (c) 2013 rehabman. All rights reserved. +// + +#ifndef _NEW_KEXT_H +#define _NEW_KEXT_H + +#include + +extern "C" +{ + // helper functions for export + void* _opnew(size_t size); + void _opdel(void* p); + void* _opnewa(size_t size); + void _opdela(void *p); +} // extern "C" + +// placement new +inline void* operator new(size_t, void* where) { return where; } + +// global scope new/delete +inline void* operator new(size_t size) { return ::_opnew(size); } +inline void operator delete(void* p) { return ::_opdel(p); } + +// global scope array new/delete +inline void* operator new[](size_t size) { return ::_opnewa(size); } +inline void operator delete[](void *p) { return ::_opdela(p); } + +//REVIEW: seems that IOMallocAligned is broken in OS X... don't use it for now! +#define IOMallocAligned(x,y) IOMalloc(x) +#define IOFreeAligned(x,y) IOFree(x,y) + +#endif // _NEW_KEXT_H