diff --git a/1002-Add-apple-ibridge-driver.patch b/1002-Add-apple-ibridge-driver.patch deleted file mode 100644 index 266dbc6..0000000 --- a/1002-Add-apple-ibridge-driver.patch +++ /dev/null @@ -1,3400 +0,0 @@ -From b044c864cdb34f56f7c6cc5a5887b35ed7563fa9 Mon Sep 17 00:00:00 2001 -From: Aditya Garg -Date: Sat, 14 May 2022 11:59:14 +0530 -Subject: [PATCH 2/2] Add apple-ibridge driver - ---- - drivers/staging/apple-ibridge/LICENSE | 339 ++++ - drivers/staging/apple-ibridge/Makefile | 20 + - drivers/staging/apple-ibridge/README.md | 35 + - drivers/staging/apple-ibridge/apple-ib-als.c | 622 +++++++ - drivers/staging/apple-ibridge/apple-ib-tb.c | 1626 +++++++++++++++++ - drivers/staging/apple-ibridge/apple-ibridge.c | 648 +++++++ - drivers/staging/apple-ibridge/apple-ibridge.h | 23 + - drivers/staging/apple-ibridge/dkms.conf | 12 + - 8 files changed, 3325 insertions(+) - create mode 100644 drivers/staging/apple-ibridge/LICENSE - create mode 100644 drivers/staging/apple-ibridge/Makefile - create mode 100644 drivers/staging/apple-ibridge/README.md - create mode 100644 drivers/staging/apple-ibridge/apple-ib-als.c - create mode 100644 drivers/staging/apple-ibridge/apple-ib-tb.c - create mode 100644 drivers/staging/apple-ibridge/apple-ibridge.c - create mode 100644 drivers/staging/apple-ibridge/apple-ibridge.h - create mode 100644 drivers/staging/apple-ibridge/dkms.conf - -diff --git a/drivers/staging/apple-ibridge/LICENSE b/drivers/staging/apple-ibridge/LICENSE -new file mode 100644 -index 000000000..23cb79033 ---- /dev/null -+++ b/drivers/staging/apple-ibridge/LICENSE -@@ -0,0 +1,339 @@ -+ GNU GENERAL PUBLIC LICENSE -+ Version 2, June 1991 -+ -+ Copyright (C) 1989, 1991 Free Software Foundation, Inc., -+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA -+ Everyone is permitted to copy and distribute verbatim copies -+ of this license document, but changing it is not allowed. -+ -+ Preamble -+ -+ The licenses for most software are designed to take away your -+freedom to share and change it. By contrast, the GNU General Public -+License is intended to guarantee your freedom to share and change free -+software--to make sure the software is free for all its users. This -+General Public License applies to most of the Free Software -+Foundation's software and to any other program whose authors commit to -+using it. (Some other Free Software Foundation software is covered by -+the GNU Lesser General Public License instead.) You can apply it to -+your programs, too. -+ -+ When we speak of free software, we are referring to freedom, not -+price. Our General Public Licenses are designed to make sure that you -+have the freedom to distribute copies of free software (and charge for -+this service if you wish), that you receive source code or can get it -+if you want it, that you can change the software or use pieces of it -+in new free programs; and that you know you can do these things. -+ -+ To protect your rights, we need to make restrictions that forbid -+anyone to deny you these rights or to ask you to surrender the rights. -+These restrictions translate to certain responsibilities for you if you -+distribute copies of the software, or if you modify it. -+ -+ For example, if you distribute copies of such a program, whether -+gratis or for a fee, you must give the recipients all the rights that -+you have. You must make sure that they, too, receive or can get the -+source code. And you must show them these terms so they know their -+rights. -+ -+ We protect your rights with two steps: (1) copyright the software, and -+(2) offer you this license which gives you legal permission to copy, -+distribute and/or modify the software. -+ -+ Also, for each author's protection and ours, we want to make certain -+that everyone understands that there is no warranty for this free -+software. If the software is modified by someone else and passed on, we -+want its recipients to know that what they have is not the original, so -+that any problems introduced by others will not reflect on the original -+authors' reputations. -+ -+ Finally, any free program is threatened constantly by software -+patents. We wish to avoid the danger that redistributors of a free -+program will individually obtain patent licenses, in effect making the -+program proprietary. To prevent this, we have made it clear that any -+patent must be licensed for everyone's free use or not licensed at all. -+ -+ The precise terms and conditions for copying, distribution and -+modification follow. -+ -+ GNU GENERAL PUBLIC LICENSE -+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION -+ -+ 0. This License applies to any program or other work which contains -+a notice placed by the copyright holder saying it may be distributed -+under the terms of this General Public License. The "Program", below, -+refers to any such program or work, and a "work based on the Program" -+means either the Program or any derivative work under copyright law: -+that is to say, a work containing the Program or a portion of it, -+either verbatim or with modifications and/or translated into another -+language. (Hereinafter, translation is included without limitation in -+the term "modification".) Each licensee is addressed as "you". -+ -+Activities other than copying, distribution and modification are not -+covered by this License; they are outside its scope. The act of -+running the Program is not restricted, and the output from the Program -+is covered only if its contents constitute a work based on the -+Program (independent of having been made by running the Program). -+Whether that is true depends on what the Program does. -+ -+ 1. You may copy and distribute verbatim copies of the Program's -+source code as you receive it, in any medium, provided that you -+conspicuously and appropriately publish on each copy an appropriate -+copyright notice and disclaimer of warranty; keep intact all the -+notices that refer to this License and to the absence of any warranty; -+and give any other recipients of the Program a copy of this License -+along with the Program. -+ -+You may charge a fee for the physical act of transferring a copy, and -+you may at your option offer warranty protection in exchange for a fee. -+ -+ 2. You may modify your copy or copies of the Program or any portion -+of it, thus forming a work based on the Program, and copy and -+distribute such modifications or work under the terms of Section 1 -+above, provided that you also meet all of these conditions: -+ -+ a) You must cause the modified files to carry prominent notices -+ stating that you changed the files and the date of any change. -+ -+ b) You must cause any work that you distribute or publish, that in -+ whole or in part contains or is derived from the Program or any -+ part thereof, to be licensed as a whole at no charge to all third -+ parties under the terms of this License. -+ -+ c) If the modified program normally reads commands interactively -+ when run, you must cause it, when started running for such -+ interactive use in the most ordinary way, to print or display an -+ announcement including an appropriate copyright notice and a -+ notice that there is no warranty (or else, saying that you provide -+ a warranty) and that users may redistribute the program under -+ these conditions, and telling the user how to view a copy of this -+ License. (Exception: if the Program itself is interactive but -+ does not normally print such an announcement, your work based on -+ the Program is not required to print an announcement.) -+ -+These requirements apply to the modified work as a whole. If -+identifiable sections of that work are not derived from the Program, -+and can be reasonably considered independent and separate works in -+themselves, then this License, and its terms, do not apply to those -+sections when you distribute them as separate works. But when you -+distribute the same sections as part of a whole which is a work based -+on the Program, the distribution of the whole must be on the terms of -+this License, whose permissions for other licensees extend to the -+entire whole, and thus to each and every part regardless of who wrote it. -+ -+Thus, it is not the intent of this section to claim rights or contest -+your rights to work written entirely by you; rather, the intent is to -+exercise the right to control the distribution of derivative or -+collective works based on the Program. -+ -+In addition, mere aggregation of another work not based on the Program -+with the Program (or with a work based on the Program) on a volume of -+a storage or distribution medium does not bring the other work under -+the scope of this License. -+ -+ 3. You may copy and distribute the Program (or a work based on it, -+under Section 2) in object code or executable form under the terms of -+Sections 1 and 2 above provided that you also do one of the following: -+ -+ a) Accompany it with the complete corresponding machine-readable -+ source code, which must be distributed under the terms of Sections -+ 1 and 2 above on a medium customarily used for software interchange; or, -+ -+ b) Accompany it with a written offer, valid for at least three -+ years, to give any third party, for a charge no more than your -+ cost of physically performing source distribution, a complete -+ machine-readable copy of the corresponding source code, to be -+ distributed under the terms of Sections 1 and 2 above on a medium -+ customarily used for software interchange; or, -+ -+ c) Accompany it with the information you received as to the offer -+ to distribute corresponding source code. (This alternative is -+ allowed only for noncommercial distribution and only if you -+ received the program in object code or executable form with such -+ an offer, in accord with Subsection b above.) -+ -+The source code for a work means the preferred form of the work for -+making modifications to it. For an executable work, complete source -+code means all the source code for all modules it contains, plus any -+associated interface definition files, plus the scripts used to -+control compilation and installation of the executable. However, as a -+special exception, the source code distributed need not include -+anything that is normally distributed (in either source or binary -+form) with the major components (compiler, kernel, and so on) of the -+operating system on which the executable runs, unless that component -+itself accompanies the executable. -+ -+If distribution of executable or object code is made by offering -+access to copy from a designated place, then offering equivalent -+access to copy the source code from the same place counts as -+distribution of the source code, even though third parties are not -+compelled to copy the source along with the object code. -+ -+ 4. You may not copy, modify, sublicense, or distribute the Program -+except as expressly provided under this License. Any attempt -+otherwise to copy, modify, sublicense or distribute the Program is -+void, and will automatically terminate your rights under this License. -+However, parties who have received copies, or rights, from you under -+this License will not have their licenses terminated so long as such -+parties remain in full compliance. -+ -+ 5. You are not required to accept this License, since you have not -+signed it. However, nothing else grants you permission to modify or -+distribute the Program or its derivative works. These actions are -+prohibited by law if you do not accept this License. Therefore, by -+modifying or distributing the Program (or any work based on the -+Program), you indicate your acceptance of this License to do so, and -+all its terms and conditions for copying, distributing or modifying -+the Program or works based on it. -+ -+ 6. Each time you redistribute the Program (or any work based on the -+Program), the recipient automatically receives a license from the -+original licensor to copy, distribute or modify the Program subject to -+these terms and conditions. You may not impose any further -+restrictions on the recipients' exercise of the rights granted herein. -+You are not responsible for enforcing compliance by third parties to -+this License. -+ -+ 7. If, as a consequence of a court judgment or allegation of patent -+infringement or for any other reason (not limited to patent issues), -+conditions are imposed on you (whether by court order, agreement or -+otherwise) that contradict the conditions of this License, they do not -+excuse you from the conditions of this License. If you cannot -+distribute so as to satisfy simultaneously your obligations under this -+License and any other pertinent obligations, then as a consequence you -+may not distribute the Program at all. For example, if a patent -+license would not permit royalty-free redistribution of the Program by -+all those who receive copies directly or indirectly through you, then -+the only way you could satisfy both it and this License would be to -+refrain entirely from distribution of the Program. -+ -+If any portion of this section is held invalid or unenforceable under -+any particular circumstance, the balance of the section is intended to -+apply and the section as a whole is intended to apply in other -+circumstances. -+ -+It is not the purpose of this section to induce you to infringe any -+patents or other property right claims or to contest validity of any -+such claims; this section has the sole purpose of protecting the -+integrity of the free software distribution system, which is -+implemented by public license practices. Many people have made -+generous contributions to the wide range of software distributed -+through that system in reliance on consistent application of that -+system; it is up to the author/donor to decide if he or she is willing -+to distribute software through any other system and a licensee cannot -+impose that choice. -+ -+This section is intended to make thoroughly clear what is believed to -+be a consequence of the rest of this License. -+ -+ 8. If the distribution and/or use of the Program is restricted in -+certain countries either by patents or by copyrighted interfaces, the -+original copyright holder who places the Program under this License -+may add an explicit geographical distribution limitation excluding -+those countries, so that distribution is permitted only in or among -+countries not thus excluded. In such case, this License incorporates -+the limitation as if written in the body of this License. -+ -+ 9. The Free Software Foundation may publish revised and/or new versions -+of the General Public License from time to time. Such new versions will -+be similar in spirit to the present version, but may differ in detail to -+address new problems or concerns. -+ -+Each version is given a distinguishing version number. If the Program -+specifies a version number of this License which applies to it and "any -+later version", you have the option of following the terms and conditions -+either of that version or of any later version published by the Free -+Software Foundation. If the Program does not specify a version number of -+this License, you may choose any version ever published by the Free Software -+Foundation. -+ -+ 10. If you wish to incorporate parts of the Program into other free -+programs whose distribution conditions are different, write to the author -+to ask for permission. For software which is copyrighted by the Free -+Software Foundation, write to the Free Software Foundation; we sometimes -+make exceptions for this. Our decision will be guided by the two goals -+of preserving the free status of all derivatives of our free software and -+of promoting the sharing and reuse of software generally. -+ -+ NO WARRANTY -+ -+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY -+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN -+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES -+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED -+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF -+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS -+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE -+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, -+REPAIR OR CORRECTION. -+ -+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING -+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR -+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, -+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING -+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED -+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY -+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER -+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE -+POSSIBILITY OF SUCH DAMAGES. -+ -+ END OF TERMS AND CONDITIONS -+ -+ How to Apply These Terms to Your New Programs -+ -+ If you develop a new program, and you want it to be of the greatest -+possible use to the public, the best way to achieve this is to make it -+free software which everyone can redistribute and change under these terms. -+ -+ To do so, attach the following notices to the program. It is safest -+to attach them to the start of each source file to most effectively -+convey the exclusion of warranty; and each file should have at least -+the "copyright" line and a pointer to where the full notice is found. -+ -+ {description} -+ Copyright (C) {year} {fullname} -+ -+ This program is free software; you can redistribute it and/or modify -+ it under the terms of the GNU General Public License as published by -+ the Free Software Foundation; either version 2 of the License, or -+ (at your option) any later version. -+ -+ This program is distributed in the hope that it will be useful, -+ but WITHOUT ANY WARRANTY; without even the implied warranty of -+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -+ GNU General Public License for more details. -+ -+ You should have received a copy of the GNU General Public License along -+ with this program; if not, write to the Free Software Foundation, Inc., -+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -+ -+Also add information on how to contact you by electronic and paper mail. -+ -+If the program is interactive, make it output a short notice like this -+when it starts in an interactive mode: -+ -+ Gnomovision version 69, Copyright (C) year name of author -+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. -+ This is free software, and you are welcome to redistribute it -+ under certain conditions; type `show c' for details. -+ -+The hypothetical commands `show w' and `show c' should show the appropriate -+parts of the General Public License. Of course, the commands you use may -+be called something other than `show w' and `show c'; they could even be -+mouse-clicks or menu items--whatever suits your program. -+ -+You should also get your employer (if you work as a programmer) or your -+school, if any, to sign a "copyright disclaimer" for the program, if -+necessary. Here is a sample; alter the names: -+ -+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program -+ `Gnomovision' (which makes passes at compilers) written by James Hacker. -+ -+ {signature of Ty Coon}, 1 April 1989 -+ Ty Coon, President of Vice -+ -+This General Public License does not permit incorporating your program into -+proprietary programs. If your program is a subroutine library, you may -+consider it more useful to permit linking proprietary applications with the -+library. If this is what you want to do, use the GNU Lesser General -+Public License instead of this License. -diff --git a/drivers/staging/apple-ibridge/Makefile b/drivers/staging/apple-ibridge/Makefile -new file mode 100644 -index 000000000..ea40afe93 ---- /dev/null -+++ b/drivers/staging/apple-ibridge/Makefile -@@ -0,0 +1,20 @@ -+obj-m += apple-ibridge.o -+obj-m += apple-ib-tb.o -+obj-m += apple-ib-als.o -+ -+KVERSION := $(KERNELRELEASE) -+ifeq ($(origin KERNELRELEASE), undefined) -+KVERSION := $(shell uname -r) -+endif -+KDIR := /lib/modules/$(KVERSION)/build -+PWD := $(shell pwd) -+ -+all: -+ $(MAKE) -C $(KDIR) M=$(PWD) modules -+ -+clean: -+ $(MAKE) -C $(KDIR) M=$(PWD) clean -+ -+install: -+ $(MAKE) -C $(KDIR) M=$(PWD) modules_install -+ -diff --git a/drivers/staging/apple-ibridge/README.md b/drivers/staging/apple-ibridge/README.md -new file mode 100644 -index 000000000..b0be5e234 ---- /dev/null -+++ b/drivers/staging/apple-ibridge/README.md -@@ -0,0 +1,35 @@ -+Work in progress driver for the touchbar and ambient-light-sensor on 2019 MacBook Pro's. -+ -+Building and Installing: -+------------------------ -+``` -+git clone --branch mbp15 https://github.com/roadrunner2/macbook12-spi-driver.git -+cd macbook12-spi-driver -+make -+sudo modprobe industrialio_triggered_buffer -+sudo insmod apple-ibridge.ko -+sudo insmod apple-ib-tb.ko -+sudo insmod apple-ib-als.ko -+``` -+ -+Alternatively, use dkms: -+ -+DKMS module: -+------------ -+As root, do the following (use `dnf` instead of `apt` if on Fedora or similar): -+``` -+apt install dkms -+git clone --branch mbp15 https://github.com/roadrunner2/macbook12-spi-driver.git /usr/src/apple-ibridge-0.1 -+dkms install -m apple-ibridge -v 0.1 -+modprobe apple-ib-tb -+modprobe apple-ib-als -+``` -+ -+Touchbar/ALS/iBridge: -+--------------------- -+The touchbar and ambient-light-sensor (ALS) are part of the iBridge (T2) chip, and hence there are 3 modules corresponding to these (`apple_ibridge`, `apple_ib_tb`, and `apple_ib_als`). Generally loading any one of these will load the others, unless you are loading them via `insmod`. If loading manually (i.e. via `insmod`), you need to first load the `industrialio_triggered_buffer` and `apple_ibridge` modules. -+ -+The touchbar driver provides basic touchbar functionality (enabling the touchbar and switching between modes based on the FN key). The touchbar is automatically dimmed and later switched off if no (internal) keyboard, touchpad, or touchbar input is received for a period of time; any (internal) keyboard, touchpad, or touchbar input switches it back on. The timeouts till the touchbar is dimmed and turned off can be changed via the `idle_timeout` and `dim_timeout` module params or sysfs attributes (`/sys/class/input/input9/device/...`); they default to 5 min and 4.5 min, respectively. See also `modinfo apple_ib_tb`. -+ -+The ALS driver exposes the ambient light sensor; if you have the `iio-sensor-proxy` installed then it should be recognized and handled automatically. -+ -diff --git a/drivers/staging/apple-ibridge/apple-ib-als.c b/drivers/staging/apple-ibridge/apple-ib-als.c -new file mode 100644 -index 000000000..3d8f8e4ea ---- /dev/null -+++ b/drivers/staging/apple-ibridge/apple-ib-als.c -@@ -0,0 +1,622 @@ -+// SPDX-License-Identifier: GPL-2.0 -+/* -+ * Apple Ambient Light Sensor Driver -+ * -+ * Copyright (c) 2017-2018 Ronald Tschalär -+ */ -+ -+/* -+ * MacBookPro models with an iBridge chip (13,[23] and 14,[23]) have an -+ * ambient light sensor that is exposed via one of the USB interfaces on -+ * the iBridge as a standard HID light sensor. However, we cannot use the -+ * existing hid-sensor-als driver, for the following reasons: -+ * -+ * 1. The hid-sensor-als driver is part of the hid-sensor-hub which in turn -+ * is a hid driver, but you can't have more than one hid driver per hid -+ * device, which is a problem because the touch bar also needs to -+ * register as a driver for this hid device. -+ * (FIXME: not true if we use multiple virtual-hdev's per sub-driver) -+ * -+ * 2. The hid-sensor-hub expects the hid reports to be wrapped in a physical -+ * collection, but this sensor has them wrapped in an application -+ * collection. -+ * -+ * 3. The hid-sensor-attributes expects the absolute sensitivity to reported -+ * with a usage of 030Fh (Property: Change Sensitivity Absolute), and -+ * hid-sensor-als also tests for 04D0h (Data Field: Light + Modifier: -+ * Change Sensitivity Absolute); however the device's report uses a usage -+ * of 14D1h (Data Field: Illuminance + Modifier: Change Sensitivity -+ * Absolute) -+ -+ * 4. The device needs to be explicitly powered up before making various -+ * requests - this may be a timing issue, but the async pm calls done -+ * by the usbhid driver do not appear to be sufficient. -+ * (FIXME: the hid-sensor-als seems to be ok here) -+ * -+ * 5. This driver implements a dynamic sensitivity mode, basically a -+ * relative-percent sensitivity. -+ */ -+ -+#define dev_fmt(fmt) "als: " fmt -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include "apple-ibridge.h" -+ -+#define APPLEALS_DYN_SENS 0 /* our dynamic sensitivity */ -+#define APPLEALS_DEF_CHANGE_SENS APPLEALS_DYN_SENS -+ -+struct appleals_device { -+ struct hid_device *hid_dev; -+ struct hid_report *cfg_report; -+ struct hid_field *illum_field; -+ struct iio_dev *iio_dev; -+ int cur_sensitivity; -+ int cur_hysteresis; -+ bool events_enabled; -+}; -+ -+static struct hid_driver appleals_hid_driver; -+ -+/* -+ * This is a primitive way to get a relative sensitivity, one where we get -+ * notified when the value changes by a certain percentage rather than some -+ * absolute value. MacOS somehow manages to configure the sensor to work this -+ * way (with a 15% relative sensitivity), but I haven't been able to figure -+ * out how so far. So until we do, this provides a less-than-perfect -+ * simulation. -+ * -+ * When the brightness value is within one of the ranges, the sensitivity is -+ * set to that range's sensitivity. But in order to reduce flapping when the -+ * brightness is right on the border between two ranges, the ranges overlap -+ * somewhat (by at least one sensitivity), and sensitivity is only changed if -+ * the value leaves the current sensitivity's range (i.e. there's a hysteresis -+ * between the individual ranges). -+ * -+ * The values chosen for the map are somewhat arbitrary: a compromise of not -+ * too many ranges (and hence changing the sensitivity too often) but not too -+ * small or large of a percentage of the min and max values in the range -+ * (currently from 7.5% to 30%, i.e. within a factor of 2 of 15%), as well as -+ * just plain "this feels reasonable to me and seems to work well". -+ */ -+struct appleals_sensitivity_map { -+ int sensitivity; -+ int illum_low; -+ int illum_high; -+}; -+ -+static const struct appleals_sensitivity_map appleals_sensitivity_map[] = { -+ { 1, 0, 14 }, -+ { 3, 10, 40 }, -+ { 9, 30, 120 }, -+ { 27, 90, 360 }, -+ { 81, 270, 1080 }, -+ { 243, 810, 3240 }, -+ { 729, 2430, 9720 }, -+}; -+ -+static int appleals_compute_sensitivity(int cur_illum, int cur_sens) -+{ -+ const struct appleals_sensitivity_map *entry; -+ int i; -+ -+ /* see if we're still in current range */ -+ for (i = 0; i < ARRAY_SIZE(appleals_sensitivity_map); i++) { -+ entry = &appleals_sensitivity_map[i]; -+ -+ if (entry->sensitivity == cur_sens && -+ entry->illum_low <= cur_illum && -+ entry->illum_high >= cur_illum) -+ return cur_sens; -+ else if (entry->sensitivity > cur_sens) -+ break; -+ } -+ -+ /* not in current range, so find new sensitivity */ -+ for (i = 0; i < ARRAY_SIZE(appleals_sensitivity_map); i++) { -+ entry = &appleals_sensitivity_map[i]; -+ -+ if (entry->illum_low <= cur_illum && -+ entry->illum_high >= cur_illum) -+ return entry->sensitivity; -+ } -+ -+ /* hmm, not in table, so assume we are above highest range */ -+ i = ARRAY_SIZE(appleals_sensitivity_map) - 1; -+ return appleals_sensitivity_map[i].sensitivity; -+} -+ -+static int appleals_get_field_value_for_usage(struct hid_field *field, -+ unsigned int usage) -+{ -+ int u; -+ -+ if (!field) -+ return -1; -+ -+ for (u = 0; u < field->maxusage; u++) { -+ if (field->usage[u].hid == usage) -+ return u + field->logical_minimum; -+ } -+ -+ return -1; -+} -+ -+static __s32 appleals_get_field_value(struct appleals_device *als_dev, -+ struct hid_field *field) -+{ -+ bool powered_on = !hid_hw_power(als_dev->hid_dev, PM_HINT_FULLON); -+ -+ hid_hw_request(als_dev->hid_dev, field->report, HID_REQ_GET_REPORT); -+ hid_hw_wait(als_dev->hid_dev); -+ -+ if (powered_on) -+ hid_hw_power(als_dev->hid_dev, PM_HINT_NORMAL); -+ -+ return field->value[0]; -+} -+ -+static void appleals_set_field_value(struct appleals_device *als_dev, -+ struct hid_field *field, __s32 value) -+{ -+ hid_set_field(field, 0, value); -+ hid_hw_request(als_dev->hid_dev, field->report, HID_REQ_SET_REPORT); -+} -+ -+static int appleals_get_config(struct appleals_device *als_dev, -+ unsigned int field_usage, __s32 *value) -+{ -+ struct hid_field *field; -+ -+ field = appleib_find_report_field(als_dev->cfg_report, field_usage); -+ if (!field) -+ return -EINVAL; -+ -+ *value = appleals_get_field_value(als_dev, field); -+ -+ return 0; -+} -+ -+static int appleals_set_config(struct appleals_device *als_dev, -+ unsigned int field_usage, __s32 value) -+{ -+ struct hid_field *field; -+ -+ field = appleib_find_report_field(als_dev->cfg_report, field_usage); -+ if (!field) -+ return -EINVAL; -+ -+ appleals_set_field_value(als_dev, field, value); -+ -+ return 0; -+} -+ -+static int appleals_set_enum_config(struct appleals_device *als_dev, -+ unsigned int field_usage, -+ unsigned int value_usage) -+{ -+ struct hid_field *field; -+ int value; -+ -+ field = appleib_find_report_field(als_dev->cfg_report, field_usage); -+ if (!field) -+ return -EINVAL; -+ -+ value = appleals_get_field_value_for_usage(field, value_usage); -+ if (value >= 0) -+ appleals_set_field_value(als_dev, field, value); -+ -+ return 0; -+} -+ -+static void appleals_update_dyn_sensitivity(struct appleals_device *als_dev, -+ __s32 value) -+{ -+ int new_sens; -+ int rc; -+ -+ new_sens = appleals_compute_sensitivity(value, -+ als_dev->cur_sensitivity); -+ if (new_sens != als_dev->cur_sensitivity) { -+ rc = appleals_set_config(als_dev, -+ HID_USAGE_SENSOR_LIGHT_ILLUM | -+ HID_USAGE_SENSOR_DATA_MOD_CHANGE_SENSITIVITY_ABS, -+ new_sens); -+ if (!rc) -+ als_dev->cur_sensitivity = new_sens; -+ } -+} -+ -+static void appleals_push_new_value(struct appleals_device *als_dev, -+ __s32 value) -+{ -+ __s32 buf[2] = { value, value }; -+ -+ iio_push_to_buffers(als_dev->iio_dev, buf); -+ -+ if (als_dev->cur_hysteresis == APPLEALS_DYN_SENS) -+ appleals_update_dyn_sensitivity(als_dev, value); -+} -+ -+static int appleals_hid_event(struct hid_device *hdev, struct hid_field *field, -+ struct hid_usage *usage, __s32 value) -+{ -+ struct appleals_device *als_dev = hid_get_drvdata(hdev); -+ -+ if ((usage->hid & HID_USAGE_PAGE) != HID_UP_SENSOR) -+ return 0; -+ -+ if (usage->hid == HID_USAGE_SENSOR_LIGHT_ILLUM) { -+ appleals_push_new_value(als_dev, value); -+ return 1; -+ } -+ -+ return 0; -+} -+ -+static int appleals_enable_events(struct iio_trigger *trig, bool enable) -+{ -+ struct appleals_device *als_dev = iio_trigger_get_drvdata(trig); -+ int value; -+ -+ appleals_set_enum_config(als_dev, HID_USAGE_SENSOR_PROP_REPORT_STATE, -+ enable ? HID_USAGE_SENSOR_PROP_REPORTING_STATE_ALL_EVENTS_ENUM : -+ HID_USAGE_SENSOR_PROP_REPORTING_STATE_NO_EVENTS_ENUM); -+ als_dev->events_enabled = enable; -+ -+ /* if the sensor was enabled, push an initial value */ -+ if (enable) { -+ value = appleals_get_field_value(als_dev, als_dev->illum_field); -+ appleals_push_new_value(als_dev, value); -+ } -+ -+ return 0; -+} -+ -+static int appleals_read_raw(struct iio_dev *iio_dev, -+ struct iio_chan_spec const *chan, -+ int *val, int *val2, long mask) -+{ -+ struct appleals_device *als_dev = iio_priv(iio_dev); -+ __s32 value; -+ int rc; -+ -+ switch (mask) { -+ case IIO_CHAN_INFO_PROCESSED: -+ *val = appleals_get_field_value(als_dev, als_dev->illum_field); -+ return IIO_VAL_INT; -+ -+ case IIO_CHAN_INFO_SAMP_FREQ: -+ rc = appleals_get_config(als_dev, -+ HID_USAGE_SENSOR_PROP_REPORT_INTERVAL, -+ &value); -+ if (rc) -+ return rc; -+ -+ /* interval is in ms; val is in HZ, val2 in µHZ */ -+ value = 1000000000 / value; -+ *val = value / 1000000; -+ *val2 = value - (*val * 1000000); -+ -+ return IIO_VAL_INT_PLUS_MICRO; -+ -+ case IIO_CHAN_INFO_HYSTERESIS: -+ if (als_dev->cur_hysteresis == APPLEALS_DYN_SENS) { -+ *val = als_dev->cur_hysteresis; -+ return IIO_VAL_INT; -+ } -+ -+ rc = appleals_get_config(als_dev, -+ HID_USAGE_SENSOR_LIGHT_ILLUM | -+ HID_USAGE_SENSOR_DATA_MOD_CHANGE_SENSITIVITY_ABS, -+ val); -+ if (!rc) { -+ als_dev->cur_sensitivity = *val; -+ als_dev->cur_hysteresis = *val; -+ } -+ return rc ? rc : IIO_VAL_INT; -+ -+ default: -+ return -EINVAL; -+ } -+} -+ -+static int appleals_write_raw(struct iio_dev *iio_dev, -+ struct iio_chan_spec const *chan, -+ int val, int val2, long mask) -+{ -+ struct appleals_device *als_dev = iio_priv(iio_dev); -+ __s32 illum; -+ int rc; -+ -+ switch (mask) { -+ case IIO_CHAN_INFO_SAMP_FREQ: -+ rc = appleals_set_config(als_dev, -+ HID_USAGE_SENSOR_PROP_REPORT_INTERVAL, -+ 1000000000 / (val * 1000000 + val2)); -+ return rc; -+ -+ case IIO_CHAN_INFO_HYSTERESIS: -+ if (val == APPLEALS_DYN_SENS) { -+ if (als_dev->cur_hysteresis != APPLEALS_DYN_SENS) { -+ als_dev->cur_hysteresis = val; -+ illum = appleals_get_field_value(als_dev, -+ als_dev->illum_field); -+ appleals_update_dyn_sensitivity(als_dev, illum); -+ } -+ -+ return 0; -+ } -+ -+ rc = appleals_set_config(als_dev, -+ HID_USAGE_SENSOR_LIGHT_ILLUM | -+ HID_USAGE_SENSOR_DATA_MOD_CHANGE_SENSITIVITY_ABS, -+ val); -+ if (!rc) { -+ als_dev->cur_sensitivity = val; -+ als_dev->cur_hysteresis = val; -+ } -+ -+ return rc; -+ -+ default: -+ return -EINVAL; -+ } -+} -+ -+static const struct iio_chan_spec appleals_channels[] = { -+ { -+ .type = IIO_INTENSITY, -+ .modified = 1, -+ .channel2 = IIO_MOD_LIGHT_BOTH, -+ .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED), -+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SAMP_FREQ) | -+ BIT(IIO_CHAN_INFO_HYSTERESIS), -+ .scan_type = { -+ .sign = 'u', -+ .realbits = 32, -+ .storagebits = 32, -+ }, -+ .scan_index = 0, -+ }, -+ { -+ .type = IIO_LIGHT, -+ .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED), -+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SAMP_FREQ) | -+ BIT(IIO_CHAN_INFO_HYSTERESIS), -+ .scan_type = { -+ .sign = 'u', -+ .realbits = 32, -+ .storagebits = 32, -+ }, -+ .scan_index = 1, -+ } -+}; -+ -+static const struct iio_trigger_ops appleals_trigger_ops = { -+ .set_trigger_state = &appleals_enable_events, -+}; -+ -+static const struct iio_info appleals_info = { -+ .read_raw = &appleals_read_raw, -+ .write_raw = &appleals_write_raw, -+}; -+ -+static void appleals_config_sensor(struct appleals_device *als_dev, -+ bool events_enabled, int sensitivity) -+{ -+ struct hid_field *field; -+ bool powered_on; -+ __s32 val; -+ -+ powered_on = !hid_hw_power(als_dev->hid_dev, PM_HINT_FULLON); -+ -+ hid_hw_request(als_dev->hid_dev, als_dev->cfg_report, -+ HID_REQ_GET_REPORT); -+ hid_hw_wait(als_dev->hid_dev); -+ -+ field = appleib_find_report_field(als_dev->cfg_report, -+ HID_USAGE_SENSOR_PROY_POWER_STATE); -+ val = appleals_get_field_value_for_usage(field, -+ HID_USAGE_SENSOR_PROP_POWER_STATE_D0_FULL_POWER_ENUM); -+ if (val >= 0) -+ hid_set_field(field, 0, val); -+ -+ field = appleib_find_report_field(als_dev->cfg_report, -+ HID_USAGE_SENSOR_PROP_REPORT_STATE); -+ val = appleals_get_field_value_for_usage(field, -+ events_enabled ? -+ HID_USAGE_SENSOR_PROP_REPORTING_STATE_ALL_EVENTS_ENUM : -+ HID_USAGE_SENSOR_PROP_REPORTING_STATE_NO_EVENTS_ENUM); -+ if (val >= 0) -+ hid_set_field(field, 0, val); -+ -+ field = appleib_find_report_field(als_dev->cfg_report, -+ HID_USAGE_SENSOR_PROP_REPORT_INTERVAL); -+ hid_set_field(field, 0, field->logical_minimum); -+ -+ /* -+ * Set initial change sensitivity; if dynamic, enabling trigger will set -+ * it instead. -+ */ -+ if (sensitivity != APPLEALS_DYN_SENS) { -+ field = appleib_find_report_field(als_dev->cfg_report, -+ HID_USAGE_SENSOR_LIGHT_ILLUM | -+ HID_USAGE_SENSOR_DATA_MOD_CHANGE_SENSITIVITY_ABS); -+ -+ hid_set_field(field, 0, sensitivity); -+ } -+ -+ hid_hw_request(als_dev->hid_dev, als_dev->cfg_report, -+ HID_REQ_SET_REPORT); -+ -+ if (powered_on) -+ hid_hw_power(als_dev->hid_dev, PM_HINT_NORMAL); -+} -+ -+static int appleals_config_iio(struct appleals_device *als_dev) -+{ -+ struct iio_dev *iio_dev = als_dev->iio_dev; -+ struct iio_trigger *iio_trig; -+ struct device *parent = &als_dev->hid_dev->dev; -+ int rc; -+ -+ iio_dev->channels = appleals_channels; -+ iio_dev->num_channels = ARRAY_SIZE(appleals_channels); -+ iio_dev->dev.parent = parent; -+ iio_dev->info = &appleals_info; -+ iio_dev->name = "als"; -+ iio_dev->modes = INDIO_DIRECT_MODE; -+ -+ rc = devm_iio_triggered_buffer_setup(parent, iio_dev, -+ &iio_pollfunc_store_time, NULL, -+ NULL); -+ if (rc) { -+ hid_err(als_dev->hid_dev, -+ "Failed to set up iio triggered buffer: %d\n", rc); -+ return rc; -+ } -+ -+ #if LINUX_VERSION_CODE < KERNEL_VERSION(5,14,0) -+ iio_trig = devm_iio_trigger_alloc(parent, "%s-dev%d", iio_dev->name, -+ iio_dev->id); -+ #else -+ iio_trig = devm_iio_trigger_alloc(parent, "%s-dev%d", -+ iio_dev->name); -+ #endif -+ if (!iio_trig) -+ return -ENOMEM; -+ -+ iio_trig->dev.parent = parent; -+ iio_trig->ops = &appleals_trigger_ops; -+ iio_trigger_set_drvdata(iio_trig, als_dev); -+ -+ rc = devm_iio_trigger_register(parent, iio_trig); -+ if (rc) { -+ hid_err(als_dev->hid_dev, -+ "Failed to register iio trigger: %d\n", -+ rc); -+ return rc; -+ } -+ -+ rc = devm_iio_device_register(parent, iio_dev); -+ if (rc) { -+ hid_err(als_dev->hid_dev, "Failed to register iio device: %d\n", -+ rc); -+ return rc; -+ } -+ -+ als_dev->iio_dev = iio_dev; -+ -+ return 0; -+} -+ -+static int appleals_probe(struct hid_device *hdev, -+ const struct hid_device_id *id) -+{ -+ struct appleals_device *als_dev; -+ struct iio_dev *iio_dev; -+ struct hid_field *state_field; -+ struct hid_field *illum_field; -+ int rc; -+ -+ /* find als fields and reports */ -+ rc = hid_parse(hdev); -+ if (rc) { -+ hid_err(hdev, "als: hid parse failed (%d)\n", rc); -+ return rc; -+ } -+ -+ state_field = appleib_find_hid_field(hdev, HID_USAGE_SENSOR_ALS, -+ HID_USAGE_SENSOR_PROP_REPORT_STATE); -+ illum_field = appleib_find_hid_field(hdev, HID_USAGE_SENSOR_ALS, -+ HID_USAGE_SENSOR_LIGHT_ILLUM); -+ if (!state_field || !illum_field) -+ return -ENODEV; -+ -+ hid_dbg(hdev, "Found ambient light sensor\n"); -+ -+ /* initialize device */ -+ iio_dev = devm_iio_device_alloc(&hdev->dev, sizeof(*als_dev)); -+ if (!iio_dev) -+ return -ENOMEM; -+ -+ als_dev = iio_priv(iio_dev); -+ -+ als_dev->hid_dev = hdev; -+ als_dev->iio_dev = iio_dev; -+ als_dev->cfg_report = state_field->report; -+ als_dev->illum_field = illum_field; -+ -+ als_dev->cur_hysteresis = APPLEALS_DEF_CHANGE_SENS; -+ als_dev->cur_sensitivity = APPLEALS_DEF_CHANGE_SENS; -+ -+ hid_set_drvdata(hdev, als_dev); -+ -+ rc = hid_hw_start(hdev, HID_CONNECT_DRIVER); -+ if (rc) { -+ hid_err(hdev, "als: hw start failed (%d)\n", rc); -+ return rc; -+ } -+ -+ hid_device_io_start(hdev); -+ appleals_config_sensor(als_dev, false, als_dev->cur_sensitivity); -+ hid_device_io_stop(hdev); -+ -+ rc = appleals_config_iio(als_dev); -+ if (rc) -+ return rc; -+ -+ return hid_hw_open(hdev); -+} -+ -+#ifdef CONFIG_PM -+static int appleals_reset_resume(struct hid_device *hdev) -+{ -+ struct appleals_device *als_dev = hid_get_drvdata(hdev); -+ __s32 illum; -+ -+ appleals_config_sensor(als_dev, als_dev->events_enabled, -+ als_dev->cur_sensitivity); -+ -+ illum = appleals_get_field_value(als_dev, als_dev->illum_field); -+ appleals_push_new_value(als_dev, illum); -+ -+ return 0; -+} -+#endif -+ -+static const struct hid_device_id appleals_hid_ids[] = { -+ { HID_USB_DEVICE(USB_VENDOR_ID_LINUX_FOUNDATION, -+ USB_DEVICE_ID_IBRIDGE_ALS) }, -+ { HID_USB_DEVICE(/* USB_VENDOR_ID_APPLE */ 0x05ac, 0x8262) }, -+ { }, -+}; -+ -+MODULE_DEVICE_TABLE(hid, appleals_hid_ids); -+ -+static struct hid_driver appleals_hid_driver = { -+ .name = "apple-ib-als", -+ .id_table = appleals_hid_ids, -+ .probe = appleals_probe, -+ .event = appleals_hid_event, -+#ifdef CONFIG_PM -+ .reset_resume = appleals_reset_resume, -+#endif -+}; -+ -+module_hid_driver(appleals_hid_driver); -+ -+MODULE_AUTHOR("Ronald Tschalär"); -+MODULE_DESCRIPTION("Apple iBridge ALS driver"); -+MODULE_LICENSE("GPL v2"); -diff --git a/drivers/staging/apple-ibridge/apple-ib-tb.c b/drivers/staging/apple-ibridge/apple-ib-tb.c -new file mode 100644 -index 000000000..e3eecdf0a ---- /dev/null -+++ b/drivers/staging/apple-ibridge/apple-ib-tb.c -@@ -0,0 +1,1626 @@ -+// SPDX-License-Identifier: GPL-2.0 -+/* -+ * Apple Touch Bar Driver -+ * -+ * Copyright (c) 2017-2018 Ronald Tschalär -+ */ -+ -+/* -+ * Recent MacBookPro models (13,[23] and 14,[23]) have a touch bar, which -+ * is exposed via several USB interfaces. MacOS supports a fancy mode -+ * where arbitrary buttons can be defined; this driver currently only -+ * supports the simple mode that consists of 3 predefined layouts -+ * (escape-only, esc + special keys, and esc + function keys). -+ * -+ * The first USB HID interface supports two reports, an input report that -+ * is used to report the key presses, and an output report which can be -+ * used to set the touch bar "mode": touch bar off (in which case no touches -+ * are reported at all), escape key only, escape + 12 function keys, and -+ * escape + several special keys (including brightness, audio volume, -+ * etc). The second interface supports several, complex reports, most of -+ * which are unknown at this time, but one of which has been determined to -+ * allow for controlling of the touch bar's brightness: off (though touches -+ * are still reported), dimmed, and full brightness. This driver makes -+ * use of these two reports. -+ */ -+ -+#define dev_fmt(fmt) "tb: " fmt -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include "apple-ibridge.h" -+ -+#define HID_UP_APPLE 0xff120000 -+#define HID_USAGE_MODE (HID_UP_CUSTOM | 0x0004) -+#define HID_USAGE_APPLE_APP (HID_UP_APPLE | 0x0001) -+#define HID_USAGE_DISP (HID_UP_APPLE | 0x0021) -+#define HID_USAGE_DISP_AUX1 (HID_UP_APPLE | 0x0020) -+ -+#define APPLETB_MAX_TB_KEYS 13 /* ESC, F1-F12 */ -+ -+#define APPLETB_CMD_MODE_ESC 0 -+#define APPLETB_CMD_MODE_FN 1 -+#define APPLETB_CMD_MODE_SPCL 2 -+#define APPLETB_CMD_MODE_OFF 3 -+#define APPLETB_CMD_MODE_UPD 254 -+#define APPLETB_CMD_MODE_NONE 255 -+ -+#define APPLETB_CMD_DISP_ON 1 -+#define APPLETB_CMD_DISP_DIM 2 -+#define APPLETB_CMD_DISP_OFF 4 -+#define APPLETB_CMD_DISP_UPD 254 -+#define APPLETB_CMD_DISP_NONE 255 -+ -+#define APPLETB_FN_MODE_FKEYS 0 -+#define APPLETB_FN_MODE_NORM 1 -+#define APPLETB_FN_MODE_INV 2 -+#define APPLETB_FN_MODE_SPCL 3 -+#define APPLETB_FN_MODE_ESC 4 -+#define APPLETB_FN_MODE_MAX APPLETB_FN_MODE_ESC -+ -+#define APPLETB_DEVID_KEYBOARD 1 -+#define APPLETB_DEVID_TOUCHPAD 2 -+ -+#define APPLETB_MAX_DIM_TIME 30 -+ -+#define APPLE_MAGIC_KBD_BL_MAX 60 -+ -+static int appletb_tb_def_idle_timeout = 5 * 60; -+module_param_named(idle_timeout, appletb_tb_def_idle_timeout, int, 0444); -+MODULE_PARM_DESC(idle_timeout, "Default touch bar idle timeout:\n" -+ " >0 - turn touch bar display off after no keyboard, trackpad, or touch bar input has been received for this many seconds;\n" -+ " the display will be turned back on as soon as new input is received\n" -+ " 0 - turn touch bar display off (input does not turn it on again)\n" -+ " -1 - turn touch bar display on (does not turn off automatically)\n" -+ " -2 - disable touch bar completely"); -+ -+static int appletb_tb_def_dim_timeout = -2; -+module_param_named(dim_timeout, appletb_tb_def_dim_timeout, int, 0444); -+MODULE_PARM_DESC(dim_timeout, "Default touch bar dim timeout:\n" -+ " >0 - dim touch bar display after no keyboard, trackpad, or touch bar input has been received for this many seconds\n" -+ " the display will be returned to full brightness as soon as new input is received\n" -+ " 0 - dim touch bar display (input does not return it to full brightness)\n" -+ " -1 - disable timeout (touch bar never dimmed)\n" -+ " [-2] - calculate timeout based on idle-timeout"); -+ -+static int appletb_tb_def_fn_mode = APPLETB_FN_MODE_NORM; -+module_param_named(fnmode, appletb_tb_def_fn_mode, int, 0444); -+MODULE_PARM_DESC(fnmode, "Default Fn key mode:\n" -+ " 0 - function-keys only\n" -+ " [1] - fn key switches from special to function-keys\n" -+ " 2 - inverse of 1\n" -+ " 3 - special keys only\n" -+ " 4 - escape key only"); -+ -+static ssize_t idle_timeout_show(struct device *dev, -+ struct device_attribute *attr, char *buf); -+static ssize_t idle_timeout_store(struct device *dev, -+ struct device_attribute *attr, -+ const char *buf, size_t size); -+static DEVICE_ATTR_RW(idle_timeout); -+ -+static ssize_t dim_timeout_show(struct device *dev, -+ struct device_attribute *attr, char *buf); -+static ssize_t dim_timeout_store(struct device *dev, -+ struct device_attribute *attr, -+ const char *buf, size_t size); -+static DEVICE_ATTR_RW(dim_timeout); -+ -+static ssize_t fnmode_show(struct device *dev, struct device_attribute *attr, -+ char *buf); -+static ssize_t fnmode_store(struct device *dev, struct device_attribute *attr, -+ const char *buf, size_t size); -+static DEVICE_ATTR_RW(fnmode); -+ -+static bool use_correct_report = false; -+static DEVICE_BOOL_ATTR(alt, 0644, use_correct_report); -+ -+static struct attribute *appletb_attrs[] = { -+ &dev_attr_idle_timeout.attr, -+ &dev_attr_dim_timeout.attr, -+ &dev_attr_fnmode.attr, -+ &dev_attr_alt.attr.attr, -+ NULL, -+}; -+ -+static const struct attribute_group appletb_attr_group = { -+ .attrs = appletb_attrs, -+}; -+ -+struct apple_magic_backlight { -+ struct led_classdev cdev; -+ struct usb_device *dev; -+ bool powered; -+}; -+ -+struct appletb_device { -+ bool active; -+ struct device *log_dev; -+ -+ struct hid_field *mode_field; -+ struct hid_field *disp_field; -+ struct hid_field *disp_field_aux1; -+ struct appletb_iface_info { -+ struct hid_device *hdev; -+ struct usb_interface *usb_iface; -+ bool suspended; -+ } mode_iface, disp_iface; -+ -+ struct input_handler inp_handler; -+ struct input_handle kbd_handle; -+ struct input_handle tpd_handle; -+ -+ bool last_tb_keys_pressed[APPLETB_MAX_TB_KEYS]; -+ bool last_tb_keys_translated[APPLETB_MAX_TB_KEYS]; -+ bool last_fn_pressed; -+ -+ ktime_t last_event_time; -+ -+ unsigned char cur_tb_mode; -+ unsigned char pnd_tb_mode; -+ unsigned char cur_tb_disp; -+ unsigned char pnd_tb_disp; -+ bool tb_autopm_off; -+ bool restore_autopm; -+ struct delayed_work tb_work; -+ /* protects most of the above */ -+ spinlock_t tb_lock; -+ -+ int dim_timeout; -+ int idle_timeout; -+ bool dim_to_is_calc; -+ int fn_mode; -+ -+ bool is_t2; -+ -+ struct apple_magic_backlight kbd_backlight; -+}; -+ -+struct appletb_key_translation { -+ u16 from; -+ u16 to; -+}; -+ -+static const struct appletb_key_translation appletb_fn_codes[] = { -+ { KEY_F1, KEY_BRIGHTNESSDOWN }, -+ { KEY_F2, KEY_BRIGHTNESSUP }, -+ { KEY_F3, KEY_SCALE }, /* not used */ -+ { KEY_F4, KEY_DASHBOARD }, /* not used */ -+ { KEY_F5, KEY_KBDILLUMDOWN }, -+ { KEY_F6, KEY_KBDILLUMUP }, -+ { KEY_F7, KEY_PREVIOUSSONG }, -+ { KEY_F8, KEY_PLAYPAUSE }, -+ { KEY_F9, KEY_NEXTSONG }, -+ { KEY_F10, KEY_MUTE }, -+ { KEY_F11, KEY_VOLUMEDOWN }, -+ { KEY_F12, KEY_VOLUMEUP }, -+}; -+ -+struct apple_magic_keyboard_backlight_brightness_report { -+ u8 report_id; /* 0x01 */ -+ u8 mode; /* If 0x00, brightness can turn off backlight */ -+ u8 brightness; -+ u8 override_1; /* If these are non-zero, backlight is overridden to max brightness */ -+ u8 override_2; -+ u8 max; /* Lower is brighter, only takes effect when turning backlight -+ * on from off, can be unreliable -+ */ -+ u8 rate; -+ u8 magic_1; /* If these are non-zero, we are ignored. */ -+ u8 magic_2; -+}; -+ -+struct apple_magic_keyboard_backlight_power_report { -+ u8 report_id; /* 0x03 */ -+ u8 power; -+ u8 max; /* Lower is brighter, only takes effect when turning backlight -+ * on from off, can be unreliable -+ */ -+ u8 rate; -+ u8 magic_1; /* If these are non-zero, we are ignored. */ -+ u8 magic_2; -+}; -+static struct appletb_device *appletb_dev; -+ -+static int appletb_send_usb_ctrl(struct appletb_iface_info *iface_info, -+ __u8 requesttype, struct hid_report *report, -+ void *data, __u16 size) -+{ -+ struct usb_device *dev = interface_to_usbdev(iface_info->usb_iface); -+ u8 ifnum = iface_info->usb_iface->cur_altsetting->desc.bInterfaceNumber; -+ int tries = 0; -+ int rc; -+ -+ do { -+ rc = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), -+ HID_REQ_SET_REPORT, requesttype, -+ (report->type + 1) << 8 | report->id, -+ ifnum, data, size, 2000); -+ if (rc != -EPIPE) -+ break; -+ -+ usleep_range(1000 << tries, 3000 << tries); -+ } while (++tries < 5); -+ -+ return (rc > 0) ? 0 : rc; -+} -+ -+static bool appletb_disable_autopm(struct hid_device *hdev) -+{ -+ int rc; -+ -+ rc = hid_hw_power(hdev, PM_HINT_FULLON); -+ -+ if (rc == 0) -+ return true; -+ -+ hid_err(hdev, -+ "Failed to disable auto-pm on touch bar device (%d)\n", rc); -+ return false; -+} -+ -+/* -+ * While the mode functionality is listed as a valid hid report in the usb -+ * interface descriptor, it's not sent that way. Instead it's sent with -+ * different request-type and without a leading report-id in the data. Hence -+ * we need to send it as a custom usb control message rather via any of the -+ * standard hid_hw_*request() functions. -+ */ -+static int appletb_set_tb_mode(struct appletb_device *tb_dev, -+ unsigned char mode) -+{ -+ struct hid_report *report; -+ void *buf; -+ bool autopm_off = false; -+ int rc; -+ -+ if (!tb_dev->mode_iface.hdev) -+ return -ENOTCONN; -+ -+ report = tb_dev->mode_field->report; -+ -+ if (tb_dev->is_t2) { -+ char data[] = { use_correct_report ? report->id : 0, mode }; -+ buf = kmemdup(data, sizeof(data), GFP_KERNEL); -+ } else { -+ buf = kmemdup(&mode, 1, GFP_KERNEL); -+ } -+ if (!buf) -+ return -ENOMEM; -+ -+ autopm_off = appletb_disable_autopm(tb_dev->mode_iface.hdev); -+ -+ if (tb_dev->is_t2) -+ rc = appletb_send_usb_ctrl(&tb_dev->mode_iface, -+ USB_DIR_OUT | USB_TYPE_CLASS | -+ USB_RECIP_INTERFACE, -+ report, buf, 2); -+ else -+ rc = appletb_send_usb_ctrl(&tb_dev->mode_iface, -+ USB_DIR_OUT | USB_TYPE_VENDOR | -+ USB_RECIP_DEVICE, -+ report, buf, 1); -+ if (rc < 0) -+ dev_err(tb_dev->log_dev, -+ "Failed to set touch bar mode to %u (%d)\n", mode, rc); -+ -+ if (autopm_off) -+ hid_hw_power(tb_dev->mode_iface.hdev, PM_HINT_NORMAL); -+ -+ kfree(buf); -+ -+ return rc; -+} -+ -+/* -+ * We don't use hid_hw_request() because that doesn't allow us to get the -+ * returned status from the usb-control request; we also don't use -+ * hid_hw_raw_request() because would mean duplicating the retry-on-EPIPE -+ * in our appletb_send_usb_ctrl(). -+ */ -+static int appletb_send_hid_report(struct appletb_iface_info *iface_info, -+ struct hid_report *report) -+{ -+ unsigned char *buf; -+ int rc; -+ -+ buf = hid_alloc_report_buf(report, GFP_KERNEL); -+ if (!buf) -+ return -ENOMEM; -+ -+ hid_output_report(report, buf); -+ -+ rc = appletb_send_usb_ctrl(iface_info, -+ USB_DIR_OUT | USB_TYPE_CLASS | -+ USB_RECIP_INTERFACE, -+ report, buf, hid_report_len(report)); -+ -+ kfree(buf); -+ -+ return rc; -+} -+ -+static int apple_magic_keyboard_backlight_power_set(struct apple_magic_backlight *backlight, -+ char power, char rate) -+{ -+ int tries = 0; -+ int rc; -+ struct apple_magic_keyboard_backlight_power_report *rep; -+ -+ rep = kmalloc(sizeof(*rep), GFP_KERNEL); -+ if (rep == NULL) -+ return -ENOMEM; -+ -+ backlight->powered = power ? true : false; -+ -+ rep->report_id = 0x03; -+ rep->power = power; -+ rep->max = 0x5e; /* Windows uses 0x5e when turning on, and 0xf4 when -+ * turning off. When it's off it doesn't matter, so -+ * use 0x5e -+ */ -+ rep->rate = rate; -+ -+ do { -+ /* -+ * FIXME: use appletb_send_hid_report, don't hard code all of this -+ * Need to get apple_tb_send_hid_report to use wIndex=0x01 -+ */ -+ rc = usb_control_msg(backlight->dev, -+ usb_sndctrlpipe(backlight->dev, 0), -+ HID_REQ_SET_REPORT, USB_DIR_OUT | -+ USB_TYPE_CLASS | USB_RECIP_INTERFACE, -+ 0x0303, 0x01, rep, sizeof(*rep), -+ 2000); -+ if (rc != -EPIPE) -+ break; -+ -+ usleep_range(1000 << tries, 3000 << tries); -+ } while (++tries < 5); -+ -+ kfree(rep); -+ return (rc > 0) ? 0 : rc; -+} -+ -+static int apple_magic_keyboard_backlight_brightness_set(struct apple_magic_backlight *backlight, -+ char brightness, char rate) -+{ -+ int tries = 0; -+ int rc; -+ struct apple_magic_keyboard_backlight_brightness_report *rep; -+ -+ rep = kmalloc(sizeof(*rep), GFP_KERNEL); -+ if (rep == NULL) -+ return -ENOMEM; -+ -+ rep->report_id = 0x01; -+ rep->mode = brightness; -+ rep->brightness = brightness; -+ rep->max = 0x5e; -+ rep->rate = rate; -+ -+ do { -+ /* -+ * FIXME: use appletb_send_hid_report, don't hard code all of this -+ * Need to get apple_tb_send_hid_report to use wIndex=0x01 -+ */ -+ rc = usb_control_msg(backlight->dev, -+ usb_sndctrlpipe(backlight->dev, 0), -+ HID_REQ_SET_REPORT, USB_DIR_OUT | -+ USB_TYPE_CLASS | USB_RECIP_INTERFACE, -+ 0x0301, 0x01, rep, sizeof(*rep), -+ 2000); -+ if (rc != -EPIPE) -+ break; -+ -+ usleep_range(1000 << tries, 3000 << tries); -+ } while (++tries < 5); -+ -+ kfree(rep); -+ return (rc > 0) ? 0 : rc; -+} -+ -+static int apple_magic_keyboard_backlight_set(struct apple_magic_backlight *backlight, -+ char brightness, char rate) -+{ -+ int rc; -+ -+ if (!brightness) -+ return apple_magic_keyboard_backlight_power_set(backlight, 0, rate); -+ -+ rc = apple_magic_keyboard_backlight_brightness_set(backlight, brightness, rate); -+ if (rc) -+ return rc; -+ -+ if (!backlight->powered && brightness) -+ rc = apple_magic_keyboard_backlight_power_set(backlight, 1, rate); -+ -+ return (rc > 0) ? 0 : rc; -+} -+ -+static int apple_magic_keyboard_backlight_led_set(struct led_classdev *led_cdev, -+ enum led_brightness brightness) -+{ -+ struct apple_magic_backlight *backlight = container_of(led_cdev, -+ struct apple_magic_backlight, cdev); -+ -+ return apple_magic_keyboard_backlight_set(backlight, brightness, 1); -+} -+ -+static int apple_magic_keyboard_backlight_init(struct appletb_device *tb_dev) -+{ -+ int ret; -+ -+ tb_dev->kbd_backlight.dev = interface_to_usbdev(tb_dev->disp_iface.usb_iface); -+ tb_dev->kbd_backlight.cdev.name = "apple::kbd_backlight"; -+ tb_dev->kbd_backlight.cdev.max_brightness = APPLE_MAGIC_KBD_BL_MAX; -+ tb_dev->kbd_backlight.cdev.brightness_set_blocking = apple_magic_keyboard_backlight_led_set; -+ -+ ret = apple_magic_keyboard_backlight_set(&tb_dev->kbd_backlight, 0, 0); -+ if (ret) { -+ dev_err(tb_dev->log_dev, -+ "Failed to initialise Magic Keyboard Backlight (%d)\n", ret); -+ return ret; -+ } -+ -+ return devm_led_classdev_register(tb_dev->log_dev, &tb_dev->kbd_backlight.cdev); -+} -+ -+static int appletb_set_tb_disp(struct appletb_device *tb_dev, -+ unsigned char disp) -+{ -+ struct hid_report *report; -+ int rc; -+ -+ if (!tb_dev->disp_iface.hdev) -+ return -ENOTCONN; -+ -+ report = tb_dev->disp_field->report; -+ -+ rc = hid_set_field(tb_dev->disp_field_aux1, 0, 1); -+ if (rc) { -+ dev_err(tb_dev->log_dev, -+ "Failed to set display report field (%d)\n", rc); -+ return rc; -+ } -+ -+ rc = hid_set_field(tb_dev->disp_field, 0, disp); -+ if (rc) { -+ dev_err(tb_dev->log_dev, -+ "Failed to set display report field (%d)\n", rc); -+ return rc; -+ } -+ -+ /* -+ * Keep the USB interface powered on while the touch bar display is on -+ * for better responsiveness. -+ */ -+ if (disp != APPLETB_CMD_DISP_OFF && !tb_dev->tb_autopm_off) -+ tb_dev->tb_autopm_off = -+ appletb_disable_autopm(report->device); -+ -+ rc = appletb_send_hid_report(&tb_dev->disp_iface, report); -+ if (rc < 0) -+ dev_err(tb_dev->log_dev, -+ "Failed to set touch bar display to %u (%d)\n", disp, -+ rc); -+ -+ if (disp == APPLETB_CMD_DISP_OFF && tb_dev->tb_autopm_off) { -+ hid_hw_power(tb_dev->disp_iface.hdev, PM_HINT_NORMAL); -+ tb_dev->tb_autopm_off = false; -+ } -+ -+ return rc; -+} -+ -+static bool appletb_any_tb_key_pressed(struct appletb_device *tb_dev) -+{ -+ return !!memchr_inv(tb_dev->last_tb_keys_pressed, 0, -+ sizeof(tb_dev->last_tb_keys_pressed)); -+} -+ -+static void appletb_schedule_tb_update(struct appletb_device *tb_dev, s64 secs) -+{ -+ schedule_delayed_work(&tb_dev->tb_work, msecs_to_jiffies(secs * 1000)); -+} -+ -+static void appletb_set_tb_worker(struct work_struct *work) -+{ -+ struct appletb_device *tb_dev = -+ container_of(work, struct appletb_device, tb_work.work); -+ s64 time_left = 0, min_timeout, time_to_off; -+ unsigned char pending_mode; -+ unsigned char pending_disp; -+ unsigned char current_disp; -+ bool restore_autopm; -+ bool any_tb_key_pressed, need_reschedule; -+ int rc1 = 1, rc2 = 1; -+ unsigned long flags; -+ -+ spin_lock_irqsave(&tb_dev->tb_lock, flags); -+ -+ /* handle explicit mode-change request */ -+ pending_mode = tb_dev->pnd_tb_mode; -+ pending_disp = tb_dev->pnd_tb_disp; -+ restore_autopm = tb_dev->restore_autopm; -+ -+ spin_unlock_irqrestore(&tb_dev->tb_lock, flags); -+ -+ if (pending_mode != APPLETB_CMD_MODE_NONE) -+ rc1 = appletb_set_tb_mode(tb_dev, pending_mode); -+ if (pending_mode != APPLETB_CMD_MODE_NONE && -+ pending_disp != APPLETB_CMD_DISP_NONE) -+ msleep(25); -+ if (pending_disp != APPLETB_CMD_DISP_NONE) -+ rc2 = appletb_set_tb_disp(tb_dev, pending_disp); -+ -+ if (restore_autopm && tb_dev->tb_autopm_off) -+ appletb_disable_autopm(tb_dev->disp_field->report->device); -+ -+ spin_lock_irqsave(&tb_dev->tb_lock, flags); -+ -+ need_reschedule = false; -+ -+ if (rc1 == 0) { -+ tb_dev->cur_tb_mode = pending_mode; -+ -+ if (tb_dev->pnd_tb_mode == pending_mode) -+ tb_dev->pnd_tb_mode = APPLETB_CMD_MODE_NONE; -+ else -+ need_reschedule = true; -+ } -+ -+ if (rc2 == 0) { -+ tb_dev->cur_tb_disp = pending_disp; -+ -+ if (tb_dev->pnd_tb_disp == pending_disp) -+ tb_dev->pnd_tb_disp = APPLETB_CMD_DISP_NONE; -+ else -+ need_reschedule = true; -+ } -+ current_disp = tb_dev->cur_tb_disp; -+ -+ tb_dev->restore_autopm = false; -+ -+ /* calculate time left to next timeout */ -+ if (tb_dev->idle_timeout == -2 || tb_dev->idle_timeout == 0) -+ min_timeout = -1; -+ else if (tb_dev->idle_timeout == -1) -+ min_timeout = tb_dev->dim_timeout; -+ else if (tb_dev->dim_timeout <= 0) -+ min_timeout = tb_dev->idle_timeout; -+ else -+ min_timeout = min(tb_dev->dim_timeout, tb_dev->idle_timeout); -+ -+ if (min_timeout > 0) { -+ s64 idle_time = -+ (ktime_ms_delta(ktime_get(), tb_dev->last_event_time) + -+ 500) / 1000; -+ -+ time_left = max(min_timeout - idle_time, 0LL); -+ if (tb_dev->idle_timeout <= 0) -+ time_to_off = -1; -+ else if (idle_time >= tb_dev->idle_timeout) -+ time_to_off = 0; -+ else -+ time_to_off = tb_dev->idle_timeout - idle_time; -+ } else { -+ /* not used - just to appease the compiler */ -+ time_to_off = 0; -+ } -+ -+ any_tb_key_pressed = appletb_any_tb_key_pressed(tb_dev); -+ -+ spin_unlock_irqrestore(&tb_dev->tb_lock, flags); -+ -+ dev_dbg(tb_dev->log_dev, "timeout calc: idle_timeout=%d dim_timeout=%d min_timeout=%lld time_left=%lld need_reschedule=%d any_tb_key_pressed=%d\n", -+ tb_dev->idle_timeout, tb_dev->dim_timeout, min_timeout, -+ time_left, need_reschedule, any_tb_key_pressed); -+ -+ /* a new command arrived while we were busy - handle it */ -+ if (need_reschedule) { -+ appletb_schedule_tb_update(tb_dev, 0); -+ return; -+ } -+ -+ /* if no idle/dim timeout, we're done */ -+ if (min_timeout <= 0) -+ return; -+ -+ /* manage idle/dim timeout */ -+ if (time_left > 0) { -+ /* we fired too soon or had a mode-change - re-schedule */ -+ appletb_schedule_tb_update(tb_dev, time_left); -+ } else if (any_tb_key_pressed) { -+ /* keys are still pressed - re-schedule */ -+ appletb_schedule_tb_update(tb_dev, min_timeout); -+ } else { -+ /* dim or idle timeout reached */ -+ int next_disp = (time_to_off == 0) ? APPLETB_CMD_DISP_OFF : -+ APPLETB_CMD_DISP_DIM; -+ if (next_disp != current_disp && -+ appletb_set_tb_disp(tb_dev, next_disp) == 0) { -+ spin_lock_irqsave(&tb_dev->tb_lock, flags); -+ tb_dev->cur_tb_disp = next_disp; -+ spin_unlock_irqrestore(&tb_dev->tb_lock, flags); -+ } -+ -+ if (time_to_off > 0) -+ appletb_schedule_tb_update(tb_dev, time_to_off); -+ } -+} -+ -+static u16 appletb_fn_to_special(u16 code) -+{ -+ int idx; -+ -+ for (idx = 0; idx < ARRAY_SIZE(appletb_fn_codes); idx++) { -+ if (appletb_fn_codes[idx].from == code) -+ return appletb_fn_codes[idx].to; -+ } -+ -+ return 0; -+} -+ -+static unsigned char appletb_get_cur_tb_mode(struct appletb_device *tb_dev) -+{ -+ return tb_dev->pnd_tb_mode != APPLETB_CMD_MODE_NONE ? -+ tb_dev->pnd_tb_mode : tb_dev->cur_tb_mode; -+} -+ -+static unsigned char appletb_get_cur_tb_disp(struct appletb_device *tb_dev) -+{ -+ return tb_dev->pnd_tb_disp != APPLETB_CMD_DISP_NONE ? -+ tb_dev->pnd_tb_disp : tb_dev->cur_tb_disp; -+} -+ -+static unsigned char appletb_get_fn_tb_mode(struct appletb_device *tb_dev) -+{ -+ switch (tb_dev->fn_mode) { -+ case APPLETB_FN_MODE_ESC: -+ return APPLETB_CMD_MODE_ESC; -+ -+ case APPLETB_FN_MODE_FKEYS: -+ return APPLETB_CMD_MODE_FN; -+ -+ case APPLETB_FN_MODE_SPCL: -+ return APPLETB_CMD_MODE_SPCL; -+ -+ case APPLETB_FN_MODE_INV: -+ return (tb_dev->last_fn_pressed) ? APPLETB_CMD_MODE_SPCL : -+ APPLETB_CMD_MODE_FN; -+ -+ case APPLETB_FN_MODE_NORM: -+ default: -+ return (tb_dev->last_fn_pressed) ? APPLETB_CMD_MODE_FN : -+ APPLETB_CMD_MODE_SPCL; -+ } -+} -+ -+/* -+ * Switch touch bar mode and display when mode or display not the desired ones. -+ */ -+static void appletb_update_touchbar_no_lock(struct appletb_device *tb_dev, -+ bool force) -+{ -+ unsigned char want_mode; -+ unsigned char want_disp; -+ bool need_update = false; -+ -+ /* -+ * Calculate the new modes: -+ * idle_timeout: -+ * -2 mode/disp off -+ * -1 mode on, disp on/dim -+ * 0 mode on, disp off -+ * >0 mode on, disp off after idle_timeout seconds -+ * dim_timeout (only valid if idle_timeout > 0 || idle_timeout == -1): -+ * -1 disp never dimmed -+ * 0 disp always dimmed -+ * >0 disp dim after dim_timeout seconds -+ */ -+ if (tb_dev->idle_timeout == -2) { -+ want_mode = APPLETB_CMD_MODE_OFF; -+ want_disp = APPLETB_CMD_DISP_OFF; -+ } else { -+ want_mode = appletb_get_fn_tb_mode(tb_dev); -+ want_disp = tb_dev->idle_timeout == 0 ? APPLETB_CMD_DISP_OFF : -+ tb_dev->dim_timeout == 0 ? APPLETB_CMD_DISP_DIM : -+ APPLETB_CMD_DISP_ON; -+ } -+ -+ /* -+ * See if we need to update the touch bar, taking into account that we -+ * generally don't want to switch modes while a touch bar key is -+ * pressed. -+ */ -+ if (appletb_get_cur_tb_mode(tb_dev) != want_mode && -+ !appletb_any_tb_key_pressed(tb_dev)) { -+ tb_dev->pnd_tb_mode = want_mode; -+ need_update = true; -+ } -+ -+ if (appletb_get_cur_tb_disp(tb_dev) != want_disp && -+ (!appletb_any_tb_key_pressed(tb_dev) || -+ want_disp != APPLETB_CMD_DISP_OFF)) { -+ tb_dev->pnd_tb_disp = want_disp; -+ need_update = true; -+ } -+ -+ if (force) -+ need_update = true; -+ -+ /* schedule the update if desired */ -+ dev_dbg_ratelimited(tb_dev->log_dev, -+ "update: need_update=%d, want_mode=%d, cur-mode=%d, want_disp=%d, cur-disp=%d\n", -+ need_update, want_mode, tb_dev->cur_tb_mode, -+ want_disp, tb_dev->cur_tb_disp); -+ -+ if (need_update) { -+ cancel_delayed_work(&tb_dev->tb_work); -+ appletb_schedule_tb_update(tb_dev, 0); -+ } -+} -+ -+static void appletb_update_touchbar(struct appletb_device *tb_dev, bool force) -+{ -+ unsigned long flags; -+ -+ spin_lock_irqsave(&tb_dev->tb_lock, flags); -+ -+ if (tb_dev->active) -+ appletb_update_touchbar_no_lock(tb_dev, force); -+ -+ spin_unlock_irqrestore(&tb_dev->tb_lock, flags); -+} -+ -+static void appletb_set_idle_timeout(struct appletb_device *tb_dev, int new) -+{ -+ tb_dev->idle_timeout = new; -+ -+ if (tb_dev->dim_to_is_calc && tb_dev->idle_timeout > 0) -+ tb_dev->dim_timeout = new - min(APPLETB_MAX_DIM_TIME, new / 3); -+ else if (tb_dev->dim_to_is_calc) -+ tb_dev->dim_timeout = -1; -+} -+ -+static ssize_t idle_timeout_show(struct device *dev, -+ struct device_attribute *attr, char *buf) -+{ -+ struct appletb_device *tb_dev = dev_get_drvdata(dev); -+ -+ return snprintf(buf, PAGE_SIZE, "%d\n", tb_dev->idle_timeout); -+} -+ -+static ssize_t idle_timeout_store(struct device *dev, -+ struct device_attribute *attr, -+ const char *buf, size_t size) -+{ -+ struct appletb_device *tb_dev = dev_get_drvdata(dev); -+ long new; -+ int rc; -+ -+ rc = kstrtol(buf, 0, &new); -+ if (rc || new > INT_MAX || new < -2) -+ return -EINVAL; -+ -+ appletb_set_idle_timeout(tb_dev, new); -+ appletb_update_touchbar(tb_dev, true); -+ -+ return size; -+} -+ -+static void appletb_set_dim_timeout(struct appletb_device *tb_dev, int new) -+{ -+ if (new == -2) { -+ tb_dev->dim_to_is_calc = true; -+ appletb_set_idle_timeout(tb_dev, tb_dev->idle_timeout); -+ } else { -+ tb_dev->dim_to_is_calc = false; -+ tb_dev->dim_timeout = new; -+ } -+} -+ -+static ssize_t dim_timeout_show(struct device *dev, -+ struct device_attribute *attr, char *buf) -+{ -+ struct appletb_device *tb_dev = dev_get_drvdata(dev); -+ -+ return snprintf(buf, PAGE_SIZE, "%d\n", -+ tb_dev->dim_to_is_calc ? -2 : tb_dev->dim_timeout); -+} -+ -+static ssize_t dim_timeout_store(struct device *dev, -+ struct device_attribute *attr, -+ const char *buf, size_t size) -+{ -+ struct appletb_device *tb_dev = dev_get_drvdata(dev); -+ long new; -+ int rc; -+ -+ rc = kstrtol(buf, 0, &new); -+ if (rc || new > INT_MAX || new < -2) -+ return -EINVAL; -+ -+ appletb_set_dim_timeout(tb_dev, new); -+ appletb_update_touchbar(tb_dev, true); -+ -+ return size; -+} -+ -+static ssize_t fnmode_show(struct device *dev, struct device_attribute *attr, -+ char *buf) -+{ -+ struct appletb_device *tb_dev = dev_get_drvdata(dev); -+ -+ return snprintf(buf, PAGE_SIZE, "%d\n", tb_dev->fn_mode); -+} -+ -+static ssize_t fnmode_store(struct device *dev, struct device_attribute *attr, -+ const char *buf, size_t size) -+{ -+ struct appletb_device *tb_dev = dev_get_drvdata(dev); -+ long new; -+ int rc; -+ -+ rc = kstrtol(buf, 0, &new); -+ if (rc || new > APPLETB_FN_MODE_MAX || new < 0) -+ return -EINVAL; -+ -+ tb_dev->fn_mode = new; -+ appletb_update_touchbar(tb_dev, false); -+ -+ return size; -+} -+ -+static int appletb_tb_key_to_slot(unsigned int code) -+{ -+ switch (code) { -+ case KEY_ESC: -+ return 0; -+ case KEY_F1: -+ case KEY_F2: -+ case KEY_F3: -+ case KEY_F4: -+ case KEY_F5: -+ case KEY_F6: -+ case KEY_F7: -+ case KEY_F8: -+ case KEY_F9: -+ case KEY_F10: -+ return code - KEY_F1 + 1; -+ case KEY_F11: -+ case KEY_F12: -+ return code - KEY_F11 + 11; -+ default: -+ return -1; -+ } -+} -+ -+static int appletb_hid_event(struct hid_device *hdev, struct hid_field *field, -+ struct hid_usage *usage, __s32 value) -+{ -+ struct appletb_device *tb_dev = hid_get_drvdata(hdev); -+ unsigned int new_code = 0; -+ unsigned long flags; -+ bool send_dummy = false; -+ bool send_trnsl = false; -+ int slot; -+ int rc = 0; -+ -+ if ((usage->hid & HID_USAGE_PAGE) != HID_UP_KEYBOARD || -+ usage->type != EV_KEY) -+ return 0; -+ -+ /* -+ * Skip non-touch-bar keys. -+ * -+ * Either the touch bar itself or usbhid generate a slew of key-down -+ * events for all the meta keys. None of which we're at all interested -+ * in. -+ */ -+ slot = appletb_tb_key_to_slot(usage->code); -+ if (slot < 0) -+ return 0; -+ -+ spin_lock_irqsave(&tb_dev->tb_lock, flags); -+ -+ if (!tb_dev->active) { -+ spin_unlock_irqrestore(&tb_dev->tb_lock, flags); -+ return 0; -+ } -+ -+ new_code = appletb_fn_to_special(usage->code); -+ -+ if (value != 2) -+ tb_dev->last_tb_keys_pressed[slot] = value; -+ -+ tb_dev->last_event_time = ktime_get(); -+ -+ appletb_update_touchbar_no_lock(tb_dev, false); -+ -+ /* -+ * We want to suppress touch bar keys while the touch bar is off, but -+ * we do want to wake up the screen if it's asleep, so generate a dummy -+ * event in that case. -+ */ -+ if (tb_dev->cur_tb_mode == APPLETB_CMD_MODE_OFF || -+ tb_dev->cur_tb_disp == APPLETB_CMD_DISP_OFF) { -+ send_dummy = true; -+ rc = 1; -+ /* translate special keys */ -+ } else if (new_code && -+ ((value > 0 && -+ appletb_get_cur_tb_mode(tb_dev) == APPLETB_CMD_MODE_SPCL) -+ || -+ (value == 0 && tb_dev->last_tb_keys_translated[slot]))) { -+ tb_dev->last_tb_keys_translated[slot] = true; -+ send_trnsl = true; -+ rc = 1; -+ /* everything else handled normally */ -+ } else { -+ tb_dev->last_tb_keys_translated[slot] = false; -+ } -+ -+ spin_unlock_irqrestore(&tb_dev->tb_lock, flags); -+ -+ /* -+ * Need to send these input events outside of the lock, as otherwise -+ * we can run into the following deadlock: -+ * Task 1 Task 2 -+ * appletb_hid_event() input_event() -+ * acquire tb_lock acquire dev->event_lock -+ * input_event() appletb_inp_event() -+ * acquire dev->event_lock acquire tb_lock -+ */ -+ if (send_dummy) { -+ input_event(field->hidinput->input, EV_KEY, KEY_UNKNOWN, 1); -+ input_event(field->hidinput->input, EV_KEY, KEY_UNKNOWN, 0); -+ } else if (send_trnsl) { -+ input_event(field->hidinput->input, usage->type, new_code, -+ value); -+ } -+ -+ return rc; -+} -+ -+static void appletb_inp_event(struct input_handle *handle, unsigned int type, -+ unsigned int code, int value) -+{ -+ struct appletb_device *tb_dev = handle->private; -+ unsigned long flags; -+ -+ spin_lock_irqsave(&tb_dev->tb_lock, flags); -+ -+ if (!tb_dev->active) { -+ spin_unlock_irqrestore(&tb_dev->tb_lock, flags); -+ return; -+ } -+ -+ if (type == EV_KEY && code == KEY_FN && value != 2) -+ tb_dev->last_fn_pressed = value; -+ -+ tb_dev->last_event_time = ktime_get(); -+ -+ appletb_update_touchbar_no_lock(tb_dev, false); -+ -+ spin_unlock_irqrestore(&tb_dev->tb_lock, flags); -+} -+ -+/* Find and save the usb-device associated with the touch bar input device */ -+static struct usb_interface *appletb_get_usb_iface(struct hid_device *hdev) -+{ -+ struct device *dev = &hdev->dev; -+ -+ /* in kernel: dev && !is_usb_interface(dev) */ -+ while (dev && !(dev->type && dev->type->name && -+ !strcmp(dev->type->name, "usb_interface"))) -+ dev = dev->parent; -+ -+ return dev ? to_usb_interface(dev) : NULL; -+} -+ -+static int appletb_inp_connect(struct input_handler *handler, -+ struct input_dev *dev, -+ const struct input_device_id *id) -+{ -+ struct appletb_device *tb_dev = handler->private; -+ struct input_handle *handle; -+ int rc; -+ -+ if (id->driver_info == APPLETB_DEVID_KEYBOARD) { -+ handle = &tb_dev->kbd_handle; -+ handle->name = "tbkbd"; -+ switch (dev->id.product) { -+ case 0x0340u: /* MacBookPro16,1/4 */ -+ case 0x027eu: /* MacBookPro16,2 */ -+ case 0x027fu: /* MacBookPro16,3 */ -+ apple_magic_keyboard_backlight_init(tb_dev); -+ break; -+ } -+ } else if (id->driver_info == APPLETB_DEVID_TOUCHPAD) { -+ handle = &tb_dev->tpd_handle; -+ handle->name = "tbtpad"; -+ } else { -+ dev_err(tb_dev->log_dev, "Unknown device id (%lu)\n", -+ id->driver_info); -+ return -ENOENT; -+ } -+ -+ if (handle->dev) { -+ dev_err(tb_dev->log_dev, -+ "Duplicate connect to %s input device\n", handle->name); -+ return -EEXIST; -+ } -+ -+ handle->open = 0; -+ handle->dev = input_get_device(dev); -+ handle->handler = handler; -+ handle->private = tb_dev; -+ -+ rc = input_register_handle(handle); -+ if (rc) -+ goto err_free_dev; -+ -+ rc = input_open_device(handle); -+ if (rc) -+ goto err_unregister_handle; -+ -+ dev_dbg(tb_dev->log_dev, "Connected to %s input device\n", -+ handle == &tb_dev->kbd_handle ? "keyboard" : "touchpad"); -+ -+ return 0; -+ -+ err_unregister_handle: -+ input_unregister_handle(handle); -+ err_free_dev: -+ input_put_device(handle->dev); -+ handle->dev = NULL; -+ return rc; -+} -+ -+static void appletb_inp_disconnect(struct input_handle *handle) -+{ -+ struct appletb_device *tb_dev = handle->private; -+ -+ input_close_device(handle); -+ input_unregister_handle(handle); -+ -+ dev_dbg(tb_dev->log_dev, "Disconnected from %s input device\n", -+ handle == &tb_dev->kbd_handle ? "keyboard" : "touchpad"); -+ -+ input_put_device(handle->dev); -+ handle->dev = NULL; -+} -+ -+static int appletb_input_configured(struct hid_device *hdev, -+ struct hid_input *hidinput) -+{ -+ int idx; -+ struct input_dev *input = hidinput->input; -+ -+ /* -+ * Clear various input capabilities that are blindly set by the hid -+ * driver (usbkbd.c) -+ */ -+ memset(input->evbit, 0, sizeof(input->evbit)); -+ memset(input->keybit, 0, sizeof(input->keybit)); -+ memset(input->ledbit, 0, sizeof(input->ledbit)); -+ -+ /* set our actual capabilities */ -+ __set_bit(EV_KEY, input->evbit); -+ __set_bit(EV_REP, input->evbit); -+ __set_bit(EV_MSC, input->evbit); /* hid-input generates MSC_SCAN */ -+ -+ for (idx = 0; idx < ARRAY_SIZE(appletb_fn_codes); idx++) { -+ input_set_capability(input, EV_KEY, appletb_fn_codes[idx].from); -+ input_set_capability(input, EV_KEY, appletb_fn_codes[idx].to); -+ } -+ -+ input_set_capability(input, EV_KEY, KEY_ESC); -+ input_set_capability(input, EV_KEY, KEY_UNKNOWN); -+ -+ return 0; -+} -+ -+static struct appletb_iface_info * -+appletb_get_iface_info(struct appletb_device *tb_dev, struct hid_device *hdev) -+{ -+ if (hdev == tb_dev->mode_iface.hdev) -+ return &tb_dev->mode_iface; -+ if (hdev == tb_dev->disp_iface.hdev) -+ return &tb_dev->disp_iface; -+ return NULL; -+} -+ -+static int appletb_extract_report_and_iface_info(struct appletb_device *tb_dev, -+ struct hid_device *hdev, -+ const struct hid_device_id *id) -+{ -+ struct appletb_iface_info *iface_info; -+ struct usb_interface *usb_iface; -+ struct hid_field *field; -+ -+ field = appleib_find_hid_field(hdev, HID_GD_KEYBOARD, HID_USAGE_MODE); -+ if (field) { -+ iface_info = &tb_dev->mode_iface; -+ tb_dev->mode_field = field; -+ tb_dev->is_t2 = id->driver_data; -+ } else { -+ field = appleib_find_hid_field(hdev, HID_USAGE_APPLE_APP, -+ HID_USAGE_DISP); -+ if (!field) -+ return 0; -+ -+ iface_info = &tb_dev->disp_iface; -+ tb_dev->disp_field = field; -+ tb_dev->disp_field_aux1 = -+ appleib_find_hid_field(hdev, HID_USAGE_APPLE_APP, -+ HID_USAGE_DISP_AUX1); -+ -+ if (!tb_dev->disp_field_aux1 || -+ tb_dev->disp_field_aux1->report != -+ tb_dev->disp_field->report) { -+ dev_err(tb_dev->log_dev, -+ "Unexpected report structure for report %u in device %s\n", -+ tb_dev->disp_field->report->id, -+ dev_name(&hdev->dev)); -+ return -ENODEV; -+ } -+ } -+ -+ usb_iface = appletb_get_usb_iface(hdev); -+ if (!usb_iface) { -+ dev_err(tb_dev->log_dev, -+ "Failed to find usb interface for hid device %s\n", -+ dev_name(&hdev->dev)); -+ return -ENODEV; -+ } -+ -+ iface_info->hdev = hdev; -+ iface_info->usb_iface = usb_get_intf(usb_iface); -+ iface_info->suspended = false; -+ -+ return 1; -+} -+ -+static void appletb_clear_iface_info(struct appletb_device *tb_dev, -+ struct hid_device *hdev) -+{ -+ struct appletb_iface_info *iface_info; -+ -+ iface_info = appletb_get_iface_info(tb_dev, hdev); -+ if (iface_info) { -+ usb_put_intf(iface_info->usb_iface); -+ iface_info->usb_iface = NULL; -+ iface_info->hdev = NULL; -+ } -+} -+ -+static bool appletb_test_and_mark_active(struct appletb_device *tb_dev) -+{ -+ unsigned long flags; -+ bool activated = false; -+ -+ spin_lock_irqsave(&tb_dev->tb_lock, flags); -+ -+ if (tb_dev->mode_iface.hdev && tb_dev->disp_iface.hdev && -+ !tb_dev->active) { -+ tb_dev->active = true; -+ activated = true; -+ } -+ -+ spin_unlock_irqrestore(&tb_dev->tb_lock, flags); -+ -+ return activated; -+} -+ -+static bool appletb_test_and_mark_inactive(struct appletb_device *tb_dev, -+ struct hid_device *hdev) -+{ -+ unsigned long flags; -+ bool deactivated = false; -+ -+ spin_lock_irqsave(&tb_dev->tb_lock, flags); -+ -+ if (tb_dev->mode_iface.hdev && tb_dev->disp_iface.hdev && -+ tb_dev->active && -+ (hdev == tb_dev->mode_iface.hdev || -+ hdev == tb_dev->disp_iface.hdev)) { -+ tb_dev->active = false; -+ deactivated = true; -+ } -+ -+ spin_unlock_irqrestore(&tb_dev->tb_lock, flags); -+ -+ return deactivated; -+} -+ -+static const struct input_device_id appletb_input_devices[] = { -+ { -+ .flags = INPUT_DEVICE_ID_MATCH_BUS | -+ INPUT_DEVICE_ID_MATCH_KEYBIT, -+ .bustype = BUS_SPI, -+ .keybit = { [BIT_WORD(KEY_FN)] = BIT_MASK(KEY_FN) }, -+ .driver_info = APPLETB_DEVID_KEYBOARD, -+ }, /* Builtin SPI keyboard device */ -+ { -+ .flags = INPUT_DEVICE_ID_MATCH_BUS | -+ INPUT_DEVICE_ID_MATCH_KEYBIT, -+ .bustype = BUS_SPI, -+ .keybit = { [BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH) }, -+ .driver_info = APPLETB_DEVID_TOUCHPAD, -+ }, /* Builtin SPI touchpad device */ -+ { -+ .flags = INPUT_DEVICE_ID_MATCH_BUS | -+ INPUT_DEVICE_ID_MATCH_VENDOR | -+ INPUT_DEVICE_ID_MATCH_KEYBIT, -+ .bustype = BUS_USB, -+ .vendor = 0x05ac /* USB_VENDOR_ID_APPLE */, -+ .keybit = { [BIT_WORD(KEY_FN)] = BIT_MASK(KEY_FN) }, -+ .driver_info = APPLETB_DEVID_KEYBOARD, -+ }, /* Builtin USB keyboard device */ -+ { -+ .flags = INPUT_DEVICE_ID_MATCH_BUS | -+ INPUT_DEVICE_ID_MATCH_VENDOR | -+ INPUT_DEVICE_ID_MATCH_KEYBIT, -+ .bustype = BUS_USB, -+ .vendor = 0x05ac /* USB_VENDOR_ID_APPLE */, -+ .keybit = { [BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH) }, -+ .driver_info = APPLETB_DEVID_TOUCHPAD, -+ }, /* Builtin USB touchpad device */ -+ { }, /* Terminating zero entry */ -+}; -+ -+static bool appletb_match_internal_device(struct input_handler *handler, -+ struct input_dev *inp_dev) -+{ -+ struct device *dev = &inp_dev->dev; -+ -+ if (inp_dev->id.bustype == BUS_SPI) -+ return true; -+ -+ /* in kernel: dev && !is_usb_device(dev) */ -+ while (dev && !(dev->type && dev->type->name && -+ !strcmp(dev->type->name, "usb_device"))) -+ dev = dev->parent; -+ -+ /* -+ * Apple labels all their internal keyboards and trackpads as such, -+ * instead of maintaining an ever expanding list of product-id's we -+ * just look at the device's product name. -+ */ -+ if (dev) -+ return !!strstr(to_usb_device(dev)->product, "Internal Keyboard"); -+ -+ return false; -+} -+ -+static int appletb_probe(struct hid_device *hdev, -+ const struct hid_device_id *id) -+{ -+ struct appletb_device *tb_dev = appletb_dev; -+ unsigned long flags; -+ int rc; -+ -+ spin_lock_irqsave(&tb_dev->tb_lock, flags); -+ -+ if (!tb_dev->log_dev) -+ tb_dev->log_dev = &hdev->dev; -+ -+ spin_unlock_irqrestore(&tb_dev->tb_lock, flags); -+ -+ hid_set_drvdata(hdev, tb_dev); -+ -+ /* initialize the report info */ -+ rc = hid_parse(hdev); -+ if (rc) { -+ dev_err(tb_dev->log_dev, "als: hid parse failed (%d)\n", rc); -+ goto error; -+ } -+ -+ rc = appletb_extract_report_and_iface_info(tb_dev, hdev, id); -+ if (rc < 0) -+ goto error; -+ -+ rc = hid_hw_start(hdev, HID_CONNECT_DRIVER | HID_CONNECT_HIDINPUT); -+ if (rc) { -+ dev_err(tb_dev->log_dev, "hw start failed (%d)\n", rc); -+ goto clear_iface_info; -+ } -+ -+ rc = hid_hw_open(hdev); -+ if (rc) { -+ dev_err(tb_dev->log_dev, "hw open failed (%d)\n", rc); -+ goto stop_hid; -+ } -+ -+ /* do setup if we have both interfaces */ -+ if (appletb_test_and_mark_active(tb_dev)) { -+ /* initialize the touch bar */ -+ if (appletb_tb_def_fn_mode >= 0 && -+ appletb_tb_def_fn_mode <= APPLETB_FN_MODE_MAX) -+ tb_dev->fn_mode = appletb_tb_def_fn_mode; -+ else -+ tb_dev->fn_mode = APPLETB_FN_MODE_NORM; -+ appletb_set_idle_timeout(tb_dev, appletb_tb_def_idle_timeout); -+ appletb_set_dim_timeout(tb_dev, appletb_tb_def_dim_timeout); -+ tb_dev->last_event_time = ktime_get(); -+ -+ tb_dev->pnd_tb_mode = APPLETB_CMD_MODE_UPD; -+ tb_dev->pnd_tb_disp = APPLETB_CMD_DISP_UPD; -+ -+ appletb_update_touchbar(tb_dev, false); -+ -+ /* set up the input handler */ -+ tb_dev->inp_handler.event = appletb_inp_event; -+ tb_dev->inp_handler.connect = appletb_inp_connect; -+ tb_dev->inp_handler.disconnect = appletb_inp_disconnect; -+ tb_dev->inp_handler.name = "appletb"; -+ tb_dev->inp_handler.id_table = appletb_input_devices; -+ tb_dev->inp_handler.match = appletb_match_internal_device; -+ tb_dev->inp_handler.private = tb_dev; -+ -+ rc = input_register_handler(&tb_dev->inp_handler); -+ if (rc) { -+ dev_err(tb_dev->log_dev, -+ "Unable to register keyboard handler (%d)\n", -+ rc); -+ goto mark_inactive; -+ } -+ -+ /* initialize sysfs attributes */ -+ rc = sysfs_create_group(&tb_dev->mode_iface.hdev->dev.kobj, -+ &appletb_attr_group); -+ if (rc) { -+ dev_err(tb_dev->log_dev, -+ "Failed to create sysfs attributes (%d)\n", rc); -+ goto unreg_handler; -+ } -+ -+ dev_dbg(tb_dev->log_dev, "Touchbar activated\n"); -+ } -+ -+ return 0; -+ -+unreg_handler: -+ input_unregister_handler(&tb_dev->inp_handler); -+mark_inactive: -+ appletb_test_and_mark_inactive(tb_dev, hdev); -+ cancel_delayed_work_sync(&tb_dev->tb_work); -+ hid_hw_close(hdev); -+stop_hid: -+ hid_hw_stop(hdev); -+clear_iface_info: -+ appletb_clear_iface_info(tb_dev, hdev); -+error: -+ return rc; -+} -+ -+static void appletb_remove(struct hid_device *hdev) -+{ -+ struct appletb_device *tb_dev = hid_get_drvdata(hdev); -+ unsigned long flags; -+ -+ if (appletb_test_and_mark_inactive(tb_dev, hdev)) { -+ sysfs_remove_group(&tb_dev->mode_iface.hdev->dev.kobj, -+ &appletb_attr_group); -+ -+ input_unregister_handler(&tb_dev->inp_handler); -+ -+ cancel_delayed_work_sync(&tb_dev->tb_work); -+ appletb_set_tb_mode(tb_dev, APPLETB_CMD_MODE_OFF); -+ appletb_set_tb_disp(tb_dev, APPLETB_CMD_DISP_ON); -+ -+ if (tb_dev->tb_autopm_off) -+ hid_hw_power(tb_dev->disp_iface.hdev, PM_HINT_NORMAL); -+ -+ dev_info(tb_dev->log_dev, "Touchbar deactivated\n"); -+ } -+ -+ hid_hw_close(hdev); -+ hid_hw_stop(hdev); -+ appletb_clear_iface_info(tb_dev, hdev); -+ -+ spin_lock_irqsave(&tb_dev->tb_lock, flags); -+ -+ if (tb_dev->log_dev == &hdev->dev) { -+ if (tb_dev->mode_iface.hdev) -+ tb_dev->log_dev = &tb_dev->mode_iface.hdev->dev; -+ else if (tb_dev->disp_iface.hdev) -+ tb_dev->log_dev = &tb_dev->disp_iface.hdev->dev; -+ else -+ tb_dev->log_dev = NULL; -+ } -+ -+ spin_unlock_irqrestore(&tb_dev->tb_lock, flags); -+} -+ -+#ifdef CONFIG_PM -+static int appletb_suspend(struct hid_device *hdev, pm_message_t message) -+{ -+ struct appletb_device *tb_dev = hid_get_drvdata(hdev); -+ struct appletb_iface_info *iface_info; -+ unsigned long flags; -+ bool all_suspended = false; -+ -+ if (message.event != PM_EVENT_SUSPEND && -+ message.event != PM_EVENT_FREEZE) -+ return 0; -+ -+ /* -+ * Wait for both interfaces to be suspended and no more async work -+ * in progress. -+ */ -+ spin_lock_irqsave(&tb_dev->tb_lock, flags); -+ -+ if (!tb_dev->mode_iface.suspended && !tb_dev->disp_iface.suspended) { -+ tb_dev->active = false; -+ cancel_delayed_work(&tb_dev->tb_work); -+ } -+ -+ iface_info = appletb_get_iface_info(tb_dev, hdev); -+ if (iface_info) -+ iface_info->suspended = true; -+ -+ if ((!tb_dev->mode_iface.hdev || tb_dev->mode_iface.suspended) && -+ (!tb_dev->disp_iface.hdev || tb_dev->disp_iface.suspended)) -+ all_suspended = true; -+ -+ spin_unlock_irqrestore(&tb_dev->tb_lock, flags); -+ -+ flush_delayed_work(&tb_dev->tb_work); -+ -+ if (!all_suspended) -+ return 0; -+ -+ /* -+ * The touch bar device itself remembers the last state when suspended -+ * in some cases, but in others (e.g. when mode != off and disp == off) -+ * it resumes with a different state; furthermore it may be only -+ * partially responsive in that state. By turning both mode and disp -+ * off we ensure it is in a good state when resuming (and this happens -+ * to be the same state after booting/resuming-from-hibernate, so less -+ * special casing between the two). -+ */ -+ if (message.event == PM_EVENT_SUSPEND) { -+ appletb_set_tb_mode(tb_dev, APPLETB_CMD_MODE_OFF); -+ appletb_set_tb_disp(tb_dev, APPLETB_CMD_DISP_OFF); -+ } -+ -+ spin_lock_irqsave(&tb_dev->tb_lock, flags); -+ -+ tb_dev->cur_tb_mode = APPLETB_CMD_MODE_OFF; -+ tb_dev->cur_tb_disp = APPLETB_CMD_DISP_OFF; -+ -+ spin_unlock_irqrestore(&tb_dev->tb_lock, flags); -+ -+ dev_info(tb_dev->log_dev, "Touchbar suspended.\n"); -+ -+ return 0; -+} -+ -+static int appletb_reset_resume(struct hid_device *hdev) -+{ -+ struct appletb_device *tb_dev = hid_get_drvdata(hdev); -+ struct appletb_iface_info *iface_info; -+ unsigned long flags; -+ -+ spin_lock_irqsave(&tb_dev->tb_lock, flags); -+ -+ iface_info = appletb_get_iface_info(tb_dev, hdev); -+ if (iface_info) -+ iface_info->suspended = false; -+ -+ if ((tb_dev->mode_iface.hdev && !tb_dev->mode_iface.suspended) && -+ (tb_dev->disp_iface.hdev && !tb_dev->disp_iface.suspended)) { -+ /* -+ * Restore touch bar state. Note that autopm state is not -+ * preserved, so need explicitly restore that here. -+ */ -+ tb_dev->active = true; -+ tb_dev->restore_autopm = true; -+ tb_dev->last_event_time = ktime_get(); -+ -+ appletb_update_touchbar_no_lock(tb_dev, true); -+ -+ dev_info(tb_dev->log_dev, "Touchbar resumed.\n"); -+ } -+ -+ spin_unlock_irqrestore(&tb_dev->tb_lock, flags); -+ -+ return 0; -+} -+#endif -+ -+static struct appletb_device *appletb_alloc_device(void) -+{ -+ struct appletb_device *tb_dev; -+ -+ tb_dev = kzalloc(sizeof(*tb_dev), GFP_KERNEL); -+ if (!tb_dev) -+ return NULL; -+ -+ spin_lock_init(&tb_dev->tb_lock); -+ INIT_DELAYED_WORK(&tb_dev->tb_work, appletb_set_tb_worker); -+ -+ return tb_dev; -+} -+ -+static void appletb_free_device(struct appletb_device *tb_dev) -+{ -+ cancel_delayed_work_sync(&tb_dev->tb_work); -+ kfree(tb_dev); -+} -+ -+static const struct hid_device_id appletb_hid_ids[] = { -+ { HID_USB_DEVICE(USB_VENDOR_ID_LINUX_FOUNDATION, -+ USB_DEVICE_ID_IBRIDGE_TB) }, -+ { HID_USB_DEVICE(/* USB_VENDOR_ID_APPLE */ 0x05ac, 0x8102) }, -+ { HID_USB_DEVICE(/* USB_VENDOR_ID_APPLE */ 0x05ac, 0x8302), -+ .driver_data = 1 }, -+ { }, -+}; -+ -+MODULE_DEVICE_TABLE(hid, appletb_hid_ids); -+ -+static struct hid_driver appletb_hid_driver = { -+ .name = "apple-ib-touchbar", -+ .id_table = appletb_hid_ids, -+ .probe = appletb_probe, -+ .remove = appletb_remove, -+ .event = appletb_hid_event, -+ .input_configured = appletb_input_configured, -+#ifdef CONFIG_PM -+ .suspend = appletb_suspend, -+ .reset_resume = appletb_reset_resume, -+#endif -+}; -+ -+static int __init appletb_init(void) -+{ -+ struct appletb_device *tb_dev; -+ int rc; -+ -+ tb_dev = appletb_alloc_device(); -+ if (!tb_dev) -+ return -ENOMEM; -+ -+ appletb_dev = tb_dev; -+ -+ rc = hid_register_driver(&appletb_hid_driver); -+ if (rc) -+ goto error; -+ -+ return 0; -+ -+error: -+ appletb_free_device(tb_dev); -+ return rc; -+} -+ -+static void __exit appletb_exit(void) -+{ -+ hid_unregister_driver(&appletb_hid_driver); -+ appletb_free_device(appletb_dev); -+} -+ -+module_init(appletb_init); -+module_exit(appletb_exit); -+ -+MODULE_AUTHOR("Ronald Tschalär"); -+MODULE_DESCRIPTION("MacBookPro Touch Bar driver"); -+MODULE_LICENSE("GPL v2"); -diff --git a/drivers/staging/apple-ibridge/apple-ibridge.c b/drivers/staging/apple-ibridge/apple-ibridge.c -new file mode 100644 -index 000000000..53d870eb2 ---- /dev/null -+++ b/drivers/staging/apple-ibridge/apple-ibridge.c -@@ -0,0 +1,648 @@ -+// SPDX-License-Identifier: GPL-2.0 -+/* -+ * Apple iBridge Driver -+ * -+ * Copyright (c) 2018 Ronald Tschalär -+ */ -+ -+/** -+ * DOC: Overview -+ * -+ * MacBookPro models with a Touch Bar (13,[23] and 14,[23]) have an Apple -+ * iBridge chip (also known as T1 chip) which exposes the touch bar, -+ * built-in webcam (iSight), ambient light sensor, and Secure Enclave -+ * Processor (SEP) for TouchID. It shows up in the system as a USB device -+ * with 3 configurations: 'Default iBridge Interfaces', 'Default iBridge -+ * Interfaces(OS X)', and 'Default iBridge Interfaces(Recovery)'. While -+ * the second one is used by MacOS to provide the fancy touch bar -+ * functionality with custom buttons etc, this driver just uses the first. -+ * -+ * In the first (default after boot) configuration, 4 usb interfaces are -+ * exposed: 2 related to the webcam, and 2 USB HID interfaces representing -+ * the touch bar and the ambient light sensor. The webcam interfaces are -+ * already handled by the uvcvideo driver; furthermore, the handling of the -+ * input reports when "keys" on the touch bar are pressed is already handled -+ * properly by the generic USB HID core. This leaves the management of the -+ * touch bar modes (e.g. switching between function and special keys when the -+ * FN key is pressed), the touch bar display (dimming and turning off), the -+ * key-remapping when the FN key is pressed, and handling of the light sensor. -+ * -+ * This driver is implemented as a HID driver that creates virtual HID devices -+ * for the ALS and touch bar functionality, and the ALS and touch bar drivers -+ * are HID drivers which in turn attach to these virtual HID devices. This -+ * driver then relays the calls on the real HID devices to the virtual ones, -+ * and visa versa. -+ * -+ * Lastly, this driver also takes care of the power-management for the -+ * iBridge when suspending and resuming. -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include "apple-ibridge.h" -+ -+#ifdef UPSTREAM -+#include "../hid/usbhid/usbhid.h" -+#else -+#define hid_to_usb_dev(hid_dev) \ -+ to_usb_device((hid_dev)->dev.parent->parent) -+#endif -+ -+#define USB_ID_VENDOR_APPLE 0x05ac -+#define USB_ID_PRODUCT_IBRIDGE 0x8600 -+ -+#define APPLEIB_BASIC_CONFIG 1 -+ -+#define LOG_DEV(ib_dev) (&(ib_dev)->acpi_dev->dev) -+ -+static struct hid_device_id appleib_sub_hid_ids[] = { -+ { HID_USB_DEVICE(USB_VENDOR_ID_LINUX_FOUNDATION, -+ USB_DEVICE_ID_IBRIDGE_TB) }, -+ { HID_USB_DEVICE(USB_VENDOR_ID_LINUX_FOUNDATION, -+ USB_DEVICE_ID_IBRIDGE_ALS) }, -+}; -+ -+struct appleib_device { -+ struct acpi_device *acpi_dev; -+ acpi_handle asoc_socw; -+}; -+ -+struct appleib_hid_dev_info { -+ struct hid_device *hdev; -+ struct hid_device *sub_hdevs[ARRAY_SIZE(appleib_sub_hid_ids)]; -+}; -+ -+/** -+ * appleib_find_report_field() - Find the field in the report with the given -+ * usage. -+ * @report: the report to search -+ * @field_usage: the usage of the field to search for -+ * -+ * Returns: the hid field if found, or NULL if none found. -+ */ -+struct hid_field *appleib_find_report_field(struct hid_report *report, -+ unsigned int field_usage) -+{ -+ int f, u; -+ -+ for (f = 0; f < report->maxfield; f++) { -+ struct hid_field *field = report->field[f]; -+ -+ if (field->logical == field_usage) -+ return field; -+ -+ for (u = 0; u < field->maxusage; u++) { -+ if (field->usage[u].hid == field_usage) -+ return field; -+ } -+ } -+ -+ return NULL; -+} -+EXPORT_SYMBOL_GPL(appleib_find_report_field); -+ -+/** -+ * appleib_find_hid_field() - Search all the reports of the device for the -+ * field with the given usage. -+ * @hdev: the device whose reports to search -+ * @application: the usage of application collection that the field must -+ * belong to -+ * @field_usage: the usage of the field to search for -+ * -+ * Returns: the hid field if found, or NULL if none found. -+ */ -+struct hid_field *appleib_find_hid_field(struct hid_device *hdev, -+ unsigned int application, -+ unsigned int field_usage) -+{ -+ static const int report_types[] = { HID_INPUT_REPORT, HID_OUTPUT_REPORT, -+ HID_FEATURE_REPORT }; -+ struct hid_report *report; -+ struct hid_field *field; -+ int t; -+ -+ for (t = 0; t < ARRAY_SIZE(report_types); t++) { -+ struct list_head *report_list = -+ &hdev->report_enum[report_types[t]].report_list; -+ list_for_each_entry(report, report_list, list) { -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 18, 0) -+ if (report->application != application) -+ continue; -+#endif -+ -+ field = appleib_find_report_field(report, field_usage); -+ if (field) -+ return field; -+ } -+ } -+ -+ return NULL; -+} -+EXPORT_SYMBOL_GPL(appleib_find_hid_field); -+ -+static int appleib_hid_raw_event(struct hid_device *hdev, -+ struct hid_report *report, u8 *data, int size) -+{ -+ struct appleib_hid_dev_info *hdev_info = hid_get_drvdata(hdev); -+ int i; -+ -+ for (i = 0; i < ARRAY_SIZE(hdev_info->sub_hdevs); i++) -+ hid_input_report(hdev_info->sub_hdevs[i], report->type, data, -+ size, 0); -+ -+ return 0; -+} -+ -+static __u8 *appleib_report_fixup(struct hid_device *hdev, __u8 *rdesc, -+ unsigned int *rsize) -+{ -+ /* Some fields have a size of 64 bits, which according to HID 1.11 -+ * Section 8.4 is not valid ("An item field cannot span more than 4 -+ * bytes in a report"). Furthermore, hid_field_extract() complains -+ * when encountering such a field. So turn them into two 32-bit fields -+ * instead. -+ */ -+ -+ if (*rsize == 634 && -+ /* Usage Page 0xff12 (vendor defined) */ -+ rdesc[212] == 0x06 && rdesc[213] == 0x12 && rdesc[214] == 0xff && -+ /* Usage 0x51 */ -+ rdesc[416] == 0x09 && rdesc[417] == 0x51 && -+ /* report size 64 */ -+ rdesc[432] == 0x75 && rdesc[433] == 64 && -+ /* report count 1 */ -+ rdesc[434] == 0x95 && rdesc[435] == 1) { -+ rdesc[433] = 32; -+ rdesc[435] = 2; -+ hid_dbg(hdev, "Fixed up first 64-bit field\n"); -+ } -+ -+ if (*rsize == 634 && -+ /* Usage Page 0xff12 (vendor defined) */ -+ rdesc[212] == 0x06 && rdesc[213] == 0x12 && rdesc[214] == 0xff && -+ /* Usage 0x51 */ -+ rdesc[611] == 0x09 && rdesc[612] == 0x51 && -+ /* report size 64 */ -+ rdesc[627] == 0x75 && rdesc[628] == 64 && -+ /* report count 1 */ -+ rdesc[629] == 0x95 && rdesc[630] == 1) { -+ rdesc[628] = 32; -+ rdesc[630] = 2; -+ hid_dbg(hdev, "Fixed up second 64-bit field\n"); -+ } -+ -+ return rdesc; -+} -+ -+#ifdef CONFIG_PM -+/** -+ * appleib_forward_int_op() - Forward a hid-driver callback to all drivers on -+ * all virtual HID devices attached to the given real HID device. -+ * @hdev the real hid-device -+ * @forward a function that calls the callback on the given driver -+ * @args arguments for the forward function -+ * -+ * This is for callbacks that return a status as an int. -+ * -+ * Returns: 0 on success, or the first error returned by the @forward function. -+ */ -+static int appleib_forward_int_op(struct hid_device *hdev, -+ int (*forward)(struct hid_driver *, -+ struct hid_device *, void *), -+ void *args) -+{ -+ struct appleib_hid_dev_info *hdev_info = hid_get_drvdata(hdev); -+ struct hid_device *sub_hdev; -+ int rc = 0; -+ int i; -+ -+ for (i = 0; i < ARRAY_SIZE(hdev_info->sub_hdevs); i++) { -+ sub_hdev = hdev_info->sub_hdevs[i]; -+ if (sub_hdev->driver) { -+ rc = forward(sub_hdev->driver, sub_hdev, args); -+ if (rc) -+ break; -+ } -+ -+ break; -+ } -+ -+ return rc; -+} -+ -+static int appleib_hid_suspend_fwd(struct hid_driver *drv, -+ struct hid_device *hdev, void *args) -+{ -+ int rc = 0; -+ -+ if (drv->suspend) -+ rc = drv->suspend(hdev, *(pm_message_t *)args); -+ -+ return rc; -+} -+ -+static int appleib_hid_suspend(struct hid_device *hdev, pm_message_t message) -+{ -+ return appleib_forward_int_op(hdev, appleib_hid_suspend_fwd, &message); -+} -+ -+static int appleib_hid_resume_fwd(struct hid_driver *drv, -+ struct hid_device *hdev, void *args) -+{ -+ int rc = 0; -+ -+ if (drv->resume) -+ rc = drv->resume(hdev); -+ -+ return rc; -+} -+ -+static int appleib_hid_resume(struct hid_device *hdev) -+{ -+ return appleib_forward_int_op(hdev, appleib_hid_resume_fwd, NULL); -+} -+ -+static int appleib_hid_reset_resume_fwd(struct hid_driver *drv, -+ struct hid_device *hdev, void *args) -+{ -+ int rc = 0; -+ -+ if (drv->reset_resume) -+ rc = drv->reset_resume(hdev); -+ -+ return rc; -+} -+ -+static int appleib_hid_reset_resume(struct hid_device *hdev) -+{ -+ return appleib_forward_int_op(hdev, appleib_hid_reset_resume_fwd, NULL); -+} -+#endif /* CONFIG_PM */ -+ -+static int appleib_ll_start(struct hid_device *hdev) -+{ -+ return 0; -+} -+ -+static void appleib_ll_stop(struct hid_device *hdev) -+{ -+} -+ -+static int appleib_ll_open(struct hid_device *hdev) -+{ -+ /* -+ * This is not currently implemented, because the existing sub-drivers -+ * don't need it to be, and it seems unlikely they will ever need it to -+ * be: we call hid_hw_open() in this driver, so implementing this -+ * function here and appleib_ll_close() below would just involve the -+ * enabling/disabling of the forwarding of events to the sub-device; -+ * but since the sub-drivers only call hid_hw_open() inside their probe -+ * functions and hid_hw_close() inside their remove functions, this -+ * means we can always just forward the events and they will get -+ * delivered if a sub-driver is bound to the sub-device and not if not. -+ */ -+ return 0; -+} -+ -+static void appleib_ll_close(struct hid_device *hdev) -+{ -+ /* see comment in appleib_ll_open */ -+} -+ -+static int appleib_ll_power(struct hid_device *hdev, int level) -+{ -+ struct appleib_hid_dev_info *hdev_info = hdev->driver_data; -+ -+ return hid_hw_power(hdev_info->hdev, level); -+} -+ -+static int appleib_ll_parse(struct hid_device *hdev) -+{ -+ struct appleib_hid_dev_info *hdev_info = hdev->driver_data; -+ -+ return hid_parse_report(hdev, hdev_info->hdev->rdesc, -+ hdev_info->hdev->rsize); -+} -+ -+static void appleib_ll_request(struct hid_device *hdev, -+ struct hid_report *report, int reqtype) -+{ -+ struct appleib_hid_dev_info *hdev_info = hdev->driver_data; -+ -+ hid_hw_request(hdev_info->hdev, report, reqtype); -+} -+ -+static int appleib_ll_wait(struct hid_device *hdev) -+{ -+ struct appleib_hid_dev_info *hdev_info = hdev->driver_data; -+ -+ hid_hw_wait(hdev_info->hdev); -+ return 0; -+} -+ -+static int appleib_ll_raw_request(struct hid_device *hdev, -+ unsigned char reportnum, __u8 *buf, -+ size_t len, unsigned char rtype, int reqtype) -+{ -+ struct appleib_hid_dev_info *hdev_info = hdev->driver_data; -+ -+ return hid_hw_raw_request(hdev_info->hdev, reportnum, buf, len, rtype, -+ reqtype); -+} -+ -+static int appleib_ll_output_report(struct hid_device *hdev, __u8 *buf, -+ size_t len) -+{ -+ struct appleib_hid_dev_info *hdev_info = hdev->driver_data; -+ -+ return hid_hw_output_report(hdev_info->hdev, buf, len); -+} -+ -+static struct hid_ll_driver appleib_ll_driver = { -+ .start = appleib_ll_start, -+ .stop = appleib_ll_stop, -+ .open = appleib_ll_open, -+ .close = appleib_ll_close, -+ .power = appleib_ll_power, -+ .parse = appleib_ll_parse, -+ .request = appleib_ll_request, -+ .wait = appleib_ll_wait, -+ .raw_request = appleib_ll_raw_request, -+ .output_report = appleib_ll_output_report, -+}; -+ -+static struct hid_device * -+appleib_add_sub_dev(struct appleib_hid_dev_info *hdev_info, -+ struct hid_device_id *dev_id) -+{ -+ struct hid_device *sub_hdev; -+ int rc; -+ -+ sub_hdev = hid_allocate_device(); -+ if (IS_ERR(sub_hdev)) -+ return sub_hdev; -+ -+ sub_hdev->dev.parent = &hdev_info->hdev->dev; -+ -+ sub_hdev->bus = dev_id->bus; -+ sub_hdev->group = dev_id->group; -+ sub_hdev->vendor = dev_id->vendor; -+ sub_hdev->product = dev_id->product; -+ -+ sub_hdev->ll_driver = &appleib_ll_driver; -+ -+ snprintf(sub_hdev->name, sizeof(sub_hdev->name), -+ "iBridge Virtual HID %s/%04x:%04x", -+ dev_name(sub_hdev->dev.parent), sub_hdev->vendor, -+ sub_hdev->product); -+ -+ sub_hdev->driver_data = hdev_info; -+ -+ rc = hid_add_device(sub_hdev); -+ if (rc) { -+ hid_destroy_device(sub_hdev); -+ return ERR_PTR(rc); -+ } -+ -+ return sub_hdev; -+} -+ -+static struct appleib_hid_dev_info *appleib_add_device(struct hid_device *hdev) -+{ -+ struct appleib_hid_dev_info *hdev_info; -+ int i; -+ -+ hdev_info = devm_kzalloc(&hdev->dev, sizeof(*hdev_info), GFP_KERNEL); -+ if (!hdev_info) -+ return ERR_PTR(-ENOMEM); -+ -+ hdev_info->hdev = hdev; -+ -+ for (i = 0; i < ARRAY_SIZE(hdev_info->sub_hdevs); i++) { -+ hdev_info->sub_hdevs[i] = -+ appleib_add_sub_dev(hdev_info, &appleib_sub_hid_ids[i]); -+ -+ if (IS_ERR(hdev_info->sub_hdevs[i])) { -+ while (i-- > 0) -+ hid_destroy_device(hdev_info->sub_hdevs[i]); -+ return (void *)hdev_info->sub_hdevs[i]; -+ } -+ } -+ -+ return hdev_info; -+} -+ -+static void appleib_remove_device(struct hid_device *hdev) -+{ -+ struct appleib_hid_dev_info *hdev_info = hid_get_drvdata(hdev); -+ int i; -+ -+ for (i = 0; i < ARRAY_SIZE(hdev_info->sub_hdevs); i++) -+ hid_destroy_device(hdev_info->sub_hdevs[i]); -+ -+ hid_set_drvdata(hdev, NULL); -+} -+ -+static int appleib_hid_probe(struct hid_device *hdev, -+ const struct hid_device_id *id) -+{ -+ struct appleib_hid_dev_info *hdev_info; -+ struct usb_device *udev; -+ int rc; -+ -+ /* check and set usb config first */ -+ udev = hid_to_usb_dev(hdev); -+ -+ if (udev->actconfig->desc.bConfigurationValue != APPLEIB_BASIC_CONFIG) { -+ rc = usb_driver_set_configuration(udev, APPLEIB_BASIC_CONFIG); -+ return rc ? rc : -ENODEV; -+ } -+ -+ rc = hid_parse(hdev); -+ if (rc) { -+ hid_err(hdev, "ib: hid parse failed (%d)\n", rc); -+ goto error; -+ } -+ -+ rc = hid_hw_start(hdev, HID_CONNECT_DRIVER); -+ if (rc) { -+ hid_err(hdev, "ib: hw start failed (%d)\n", rc); -+ goto error; -+ } -+ -+ hdev_info = appleib_add_device(hdev); -+ if (IS_ERR(hdev_info)) { -+ rc = PTR_ERR(hdev_info); -+ goto stop_hw; -+ } -+ -+ hid_set_drvdata(hdev, hdev_info); -+ -+ rc = hid_hw_open(hdev); -+ if (rc) { -+ hid_err(hdev, "ib: failed to open hid: %d\n", rc); -+ goto remove_dev; -+ } -+ -+ return 0; -+ -+remove_dev: -+ appleib_remove_device(hdev); -+stop_hw: -+ hid_hw_stop(hdev); -+error: -+ return rc; -+} -+ -+static void appleib_hid_remove(struct hid_device *hdev) -+{ -+ hid_hw_close(hdev); -+ appleib_remove_device(hdev); -+ hid_hw_stop(hdev); -+} -+ -+static const struct hid_device_id appleib_hid_ids[] = { -+ { HID_USB_DEVICE(USB_ID_VENDOR_APPLE, USB_ID_PRODUCT_IBRIDGE) }, -+ { }, -+}; -+ -+static struct hid_driver appleib_hid_driver = { -+ .name = "apple-ibridge-hid", -+ .id_table = appleib_hid_ids, -+ .probe = appleib_hid_probe, -+ .remove = appleib_hid_remove, -+ .raw_event = appleib_hid_raw_event, -+ .report_fixup = appleib_report_fixup, -+#ifdef CONFIG_PM -+ .suspend = appleib_hid_suspend, -+ .resume = appleib_hid_resume, -+ .reset_resume = appleib_hid_reset_resume, -+#endif -+}; -+ -+static struct appleib_device *appleib_alloc_device(struct acpi_device *acpi_dev) -+{ -+ struct appleib_device *ib_dev; -+ acpi_status sts; -+ -+ ib_dev = devm_kzalloc(&acpi_dev->dev, sizeof(*ib_dev), GFP_KERNEL); -+ if (!ib_dev) -+ return ERR_PTR(-ENOMEM); -+ -+ ib_dev->acpi_dev = acpi_dev; -+ -+ /* get iBridge acpi power control method for suspend/resume */ -+ sts = acpi_get_handle(acpi_dev->handle, "SOCW", &ib_dev->asoc_socw); -+ if (ACPI_FAILURE(sts)) { -+ dev_err(LOG_DEV(ib_dev), -+ "Error getting handle for ASOC.SOCW method: %s\n", -+ acpi_format_exception(sts)); -+ return ERR_PTR(-ENXIO); -+ } -+ -+ /* ensure iBridge is powered on */ -+ sts = acpi_execute_simple_method(ib_dev->asoc_socw, NULL, 1); -+ if (ACPI_FAILURE(sts)) -+ dev_warn(LOG_DEV(ib_dev), "SOCW(1) failed: %s\n", -+ acpi_format_exception(sts)); -+ -+ return ib_dev; -+} -+ -+static int appleib_probe(struct acpi_device *acpi) -+{ -+ struct appleib_device *ib_dev; -+ int ret; -+ -+ ib_dev = appleib_alloc_device(acpi); -+ if (IS_ERR(ib_dev)) -+ return PTR_ERR(ib_dev); -+ -+ ret = hid_register_driver(&appleib_hid_driver); -+ if (ret) { -+ dev_err(LOG_DEV(ib_dev), "Error registering hid driver: %d\n", -+ ret); -+ return ret; -+ } -+ -+ acpi->driver_data = ib_dev; -+ -+ return 0; -+} -+ -+static int appleib_remove(struct acpi_device *acpi) -+{ -+ hid_unregister_driver(&appleib_hid_driver); -+ -+ return 0; -+} -+ -+static int appleib_suspend(struct device *dev) -+{ -+ struct appleib_device *ib_dev; -+ int rc; -+ -+ ib_dev = acpi_driver_data(to_acpi_device(dev)); -+ -+ rc = acpi_execute_simple_method(ib_dev->asoc_socw, NULL, 0); -+ if (ACPI_FAILURE(rc)) -+ dev_warn(dev, "SOCW(0) failed: %s\n", -+ acpi_format_exception(rc)); -+ -+ return 0; -+} -+ -+static int appleib_resume(struct device *dev) -+{ -+ struct appleib_device *ib_dev; -+ int rc; -+ -+ ib_dev = acpi_driver_data(to_acpi_device(dev)); -+ -+ rc = acpi_execute_simple_method(ib_dev->asoc_socw, NULL, 1); -+ if (ACPI_FAILURE(rc)) -+ dev_warn(dev, "SOCW(1) failed: %s\n", -+ acpi_format_exception(rc)); -+ -+ return 0; -+} -+ -+static const struct dev_pm_ops appleib_pm = { -+ .suspend = appleib_suspend, -+ .resume = appleib_resume, -+ .restore = appleib_resume, -+}; -+ -+static const struct acpi_device_id appleib_acpi_match[] = { -+ { "APP7777", 0 }, -+ { }, -+}; -+ -+MODULE_DEVICE_TABLE(acpi, appleib_acpi_match); -+ -+static struct acpi_driver appleib_driver = { -+ .name = "apple-ibridge", -+ .class = "apple_ibridge", -+ .owner = THIS_MODULE, -+ .ids = appleib_acpi_match, -+ .ops = { -+ .add = appleib_probe, -+ .remove = appleib_remove, -+ }, -+ .drv = { -+ .pm = &appleib_pm, -+ }, -+}; -+ -+module_acpi_driver(appleib_driver) -+ -+MODULE_AUTHOR("Ronald Tschalär"); -+MODULE_DESCRIPTION("Apple iBridge driver"); -+MODULE_LICENSE("GPL v2"); -diff --git a/drivers/staging/apple-ibridge/apple-ibridge.h b/drivers/staging/apple-ibridge/apple-ibridge.h -new file mode 100644 -index 000000000..07ded8c68 ---- /dev/null -+++ b/drivers/staging/apple-ibridge/apple-ibridge.h -@@ -0,0 +1,23 @@ -+/* SPDX-License-Identifier: GPL-2.0 */ -+/* -+ * Apple iBridge Driver -+ * -+ * Copyright (c) 2018 Ronald Tschalär -+ */ -+ -+#ifndef __LINUX_APPLE_IBRDIGE_H -+#define __LINUX_APPLE_IBRDIGE_H -+ -+#include -+ -+#define USB_VENDOR_ID_LINUX_FOUNDATION 0x1d6b -+#define USB_DEVICE_ID_IBRIDGE_TB 0x0301 -+#define USB_DEVICE_ID_IBRIDGE_ALS 0x0302 -+ -+struct hid_field *appleib_find_report_field(struct hid_report *report, -+ unsigned int field_usage); -+struct hid_field *appleib_find_hid_field(struct hid_device *hdev, -+ unsigned int application, -+ unsigned int field_usage); -+ -+#endif -diff --git a/drivers/staging/apple-ibridge/dkms.conf b/drivers/staging/apple-ibridge/dkms.conf -new file mode 100644 -index 000000000..5b01a1b40 ---- /dev/null -+++ b/drivers/staging/apple-ibridge/dkms.conf -@@ -0,0 +1,12 @@ -+PACKAGE_NAME="apple-ibridge" -+PACKAGE_VERSION="0.1" -+CLEAN="make clean" -+MAKE="make" -+BUILT_MODULE_NAME[0]="apple-ibridge" -+BUILT_MODULE_NAME[1]="apple-ib-tb" -+BUILT_MODULE_NAME[2]="apple-ib-als" -+DEST_MODULE_LOCATION[0]="/updates" -+DEST_MODULE_LOCATION[1]="/updates" -+DEST_MODULE_LOCATION[2]="/updates" -+AUTOINSTALL="yes" -+REMAKE_INITRD="yes" --- -2.34.1 - diff --git a/1002-Put-apple-bce-in-drivers-staging.patch b/1002-Put-apple-bce-in-drivers-staging.patch new file mode 100644 index 0000000..54ce047 --- /dev/null +++ b/1002-Put-apple-bce-in-drivers-staging.patch @@ -0,0 +1,70 @@ +From dc27d4db5787546ae5eacf3483f3b87f2d4fb1c1 Mon Sep 17 00:00:00 2001 +From: Redecorating <69827514+Redecorating@users.noreply.github.com> +Date: Mon, 7 Nov 2022 14:56:34 +0530 +Subject: [PATCH] Put apple-bce in drivers/staging + +--- + drivers/staging/Kconfig | 2 ++ + drivers/staging/Makefile | 1 + + drivers/staging/apple-bce/Kconfig | 18 ++++++++++++++++++ + drivers/staging/apple-bce/Makefile | 2 +- + 4 files changed, 22 insertions(+), 1 deletion(-) + create mode 100644 drivers/staging/apple-bce/Kconfig + +diff --git a/drivers/staging/Kconfig b/drivers/staging/Kconfig +index 5cfabd537..3b8e61d26 100644 +--- a/drivers/staging/Kconfig ++++ b/drivers/staging/Kconfig +@@ -80,4 +80,6 @@ source "drivers/staging/qlge/Kconfig" + + source "drivers/staging/vme_user/Kconfig" + ++source "drivers/staging/apple-bce/Kconfig" ++ + endif # STAGING +diff --git a/drivers/staging/Makefile b/drivers/staging/Makefile +index f8c3aa9c2..1e148d6c3 100644 +--- a/drivers/staging/Makefile ++++ b/drivers/staging/Makefile +@@ -29,3 +29,4 @@ obj-$(CONFIG_PI433) += pi433/ + obj-$(CONFIG_XIL_AXIS_FIFO) += axis-fifo/ + obj-$(CONFIG_FIELDBUS_DEV) += fieldbus/ + obj-$(CONFIG_QLGE) += qlge/ ++obj-$(CONFIG_APPLE_BCE) += apple-bce/ +diff --git a/drivers/staging/apple-bce/Kconfig b/drivers/staging/apple-bce/Kconfig +new file mode 100644 +index 000000000..fe92bc441 +--- /dev/null ++++ b/drivers/staging/apple-bce/Kconfig +@@ -0,0 +1,18 @@ ++config APPLE_BCE ++ tristate "Apple BCE driver (VHCI and Audio support)" ++ default m ++ depends on X86 ++ select SOUND ++ select SND ++ select SND_PCM ++ select SND_JACK ++ help ++ VHCI and audio support on Apple MacBooks with the T2 Chip. ++ This driver is divided in three components: ++ - BCE (Buffer Copy Engine): which establishes a basic communication ++ channel with the T2 chip. This component is required by the other two: ++ - VHCI (Virtual Host Controller Interface): Access to keyboard, mouse ++ and other system devices depend on this virtual USB host controller ++ - Audio: a driver for the T2 audio interface. ++ ++ If "M" is selected, the module will be called apple-bce.' +diff --git a/drivers/staging/apple-bce/Makefile b/drivers/staging/apple-bce/Makefile +index a6a656f06..8cfbd3f64 100644 +--- a/drivers/staging/apple-bce/Makefile ++++ b/drivers/staging/apple-bce/Makefile +@@ -1,5 +1,5 @@ + modname := apple-bce +-obj-m += $(modname).o ++obj-$(CONFIG_APPLE_BCE) += $(modname).o + + apple-bce-objs := apple_bce.o mailbox.o queue.o queue_dma.o vhci/vhci.o vhci/queue.o vhci/transfer.o audio/audio.o audio/protocol.o audio/protocol_bce.o audio/pcm.o + +-- +2.34.1 diff --git a/1003-Put-apple-bce-and-apple-ibridge-in-drivers-staging.patch b/1003-Put-apple-bce-and-apple-ibridge-in-drivers-staging.patch deleted file mode 100644 index ae78492..0000000 --- a/1003-Put-apple-bce-and-apple-ibridge-in-drivers-staging.patch +++ /dev/null @@ -1,111 +0,0 @@ -From 153b587ed53135eaf244144f6f8bdd5a0fe6b69e Mon Sep 17 00:00:00 2001 -From: Redecorating <69827514+Redecorating@users.noreply.github.com> -Date: Fri, 24 Dec 2021 18:12:25 +1100 -Subject: [PATCH 1/1] Put apple-bce and apple-ibridge in drivers/staging - ---- - drivers/staging/Kconfig | 4 ++++ - drivers/staging/Makefile | 2 ++ - drivers/staging/apple-bce/Kconfig | 18 ++++++++++++++++++ - drivers/staging/apple-bce/Makefile | 2 +- - drivers/staging/apple-ibridge/Kconfig | 15 +++++++++++++++ - drivers/staging/apple-ibridge/Makefile | 6 +++--- - 6 files changed, 43 insertions(+), 4 deletions(-) - create mode 100644 drivers/staging/apple-bce/Kconfig - create mode 100644 drivers/staging/apple-ibridge/Kconfig - -diff --git a/drivers/staging/Kconfig b/drivers/staging/Kconfig -index e03627a..fc121d5 100644 ---- a/drivers/staging/Kconfig -+++ b/drivers/staging/Kconfig -@@ -102,4 +102,8 @@ source "drivers/staging/qlge/Kconfig" - - source "drivers/staging/vme_user/Kconfig" - -+source "drivers/staging/apple-bce/Kconfig" -+ -+source "drivers/staging/apple-ibridge/Kconfig" -+ - endif # STAGING -diff --git a/drivers/staging/Makefile b/drivers/staging/Makefile -index c7f8d8d..debad40 100644 ---- a/drivers/staging/Makefile -+++ b/drivers/staging/Makefile -@@ -41,3 +41,5 @@ obj-$(CONFIG_XIL_AXIS_FIFO) += axis-fifo/ - obj-$(CONFIG_XIL_AXIS_FIFO) += axis-fifo/ - obj-$(CONFIG_FIELDBUS_DEV) += fieldbus/ - obj-$(CONFIG_QLGE) += qlge/ -+obj-$(CONFIG_APPLE_BCE) += apple-bce/ -+obj-$(CONFIG_APPLE_IBRIDGE) += apple-ibridge/ -diff --git a/drivers/staging/apple-bce/Kconfig b/drivers/staging/apple-bce/Kconfig -new file mode 100644 -index 0000000..bbfb89b ---- /dev/null -+++ b/drivers/staging/apple-bce/Kconfig -@@ -0,0 +1,18 @@ -+config APPLE_BCE -+ tristate "Apple BCE driver (VHCI and Audio support)" -+ default m -+ depends on X86 -+ select SOUND -+ select SND -+ select SND_PCM -+ select SND_JACK -+ help -+ VHCI and audio support on Apple MacBooks with the T2 Chip. -+ This driver is divided in three components: -+ - BCE (Buffer Copy Engine): which establishes a basic communication -+ channel with the T2 chip. This component is required by the other two: -+ - VHCI (Virtual Host Controller Interface): Access to keyboard, mouse -+ and other system devices depend on this virtual USB host controller -+ - Audio: a driver for the T2 audio interface. -+ -+ If "M" is selected, the module will be called apple-bce.' -diff --git a/drivers/staging/apple-bce/Makefile b/drivers/staging/apple-bce/Makefile -index a6a656f..8cfbd3f 100644 ---- a/drivers/staging/apple-bce/Makefile -+++ b/drivers/staging/apple-bce/Makefile -@@ -1,5 +1,5 @@ - modname := apple-bce --obj-m += $(modname).o -+obj-$(CONFIG_APPLE_BCE) += $(modname).o - - apple-bce-objs := apple_bce.o mailbox.o queue.o queue_dma.o vhci/vhci.o vhci/queue.o vhci/transfer.o audio/audio.o audio/protocol.o audio/protocol_bce.o audio/pcm.o - -diff --git a/drivers/staging/apple-ibridge/Kconfig b/drivers/staging/apple-ibridge/Kconfig -new file mode 100644 -index 0000000..0716486 ---- /dev/null -+++ b/drivers/staging/apple-ibridge/Kconfig -@@ -0,0 +1,15 @@ -+config APPLE_IBRIDGE -+ tristate "Apple iBridge driver (Touchbar and ALS support)" -+ default m -+ depends on X86 -+ select HID -+ select IIO -+ select IIO_TRIGGERED_BUFFER -+ select IIO_BUFFER -+ select ACPI_ALS -+ help -+ Driver for the Touchbar and ALS (Ambient Light Sensor) on some -+ Apple computers. -+ -+ If "M" is selected, the modules will be called apple-ibridge, -+ apple-ib-tb and apple-ib-als. -diff --git a/drivers/staging/apple-ibridge/Makefile b/drivers/staging/apple-ibridge/Makefile -index ea40afe..ae129bd 100644 ---- a/drivers/staging/apple-ibridge/Makefile -+++ b/drivers/staging/apple-ibridge/Makefile -@@ -1,6 +1,6 @@ --obj-m += apple-ibridge.o --obj-m += apple-ib-tb.o --obj-m += apple-ib-als.o -+obj-$(CONFIG_APPLE_IBRIDGE) += apple-ibridge.o -+obj-$(CONFIG_APPLE_IBRIDGE) += apple-ib-tb.o -+obj-$(CONFIG_APPLE_IBRIDGE) += apple-ib-als.o - - KVERSION := $(KERNELRELEASE) - ifeq ($(origin KERNELRELEASE), undefined) --- -2.34.1 diff --git a/1004-add-modalias-to-apple-bce.patch b/1003-add-modalias-to-apple-bce.patch similarity index 100% rename from 1004-add-modalias-to-apple-bce.patch rename to 1003-add-modalias-to-apple-bce.patch diff --git a/1004-HID-Recognize-sensors-with-application-collections-t.patch b/1004-HID-Recognize-sensors-with-application-collections-t.patch new file mode 100644 index 0000000..77941c4 --- /dev/null +++ b/1004-HID-Recognize-sensors-with-application-collections-t.patch @@ -0,0 +1,63 @@ +From 18bed33b3b1180a8af8a52dcf544b67b61786e89 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Ronald=20Tschal=C3=A4r?= +Date: Sat, 27 Feb 2021 17:26:39 -0800 +Subject: [PATCH 1/6] HID: Recognize sensors with application collections too. +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +According to HUTRR39 logical sensor devices may be nested inside +physical collections or may be specified in multiple top-level +application collections (see page 59, strategies 1 and 2). However, +the current code was only recognizing those with physical collections. + +This issue turned up in recent MacBook Pro's which define the ALS in +a top-level application collection. + +Signed-off-by: Ronald Tschalär +--- + drivers/hid/hid-core.c | 3 ++- + drivers/hid/hid-sensor-hub.c | 6 ++++-- + 2 files changed, 6 insertions(+), 3 deletions(-) + +diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c +index b7f5566e338d..8fcd663b10e2 100644 +--- a/drivers/hid/hid-core.c ++++ b/drivers/hid/hid-core.c +@@ -804,7 +804,8 @@ static void hid_scan_collection(struct hid_parser *parser, unsigned type) + int i; + + if (((parser->global.usage_page << 16) == HID_UP_SENSOR) && +- type == HID_COLLECTION_PHYSICAL) ++ (type == HID_COLLECTION_PHYSICAL || ++ type == HID_COLLECTION_APPLICATION)) + hid->group = HID_GROUP_SENSOR_HUB; + + if (hid->vendor == USB_VENDOR_ID_MICROSOFT && +diff --git a/drivers/hid/hid-sensor-hub.c b/drivers/hid/hid-sensor-hub.c +index 6abd3e2a9094..d03dc4ca095f 100644 +--- a/drivers/hid/hid-sensor-hub.c ++++ b/drivers/hid/hid-sensor-hub.c +@@ -397,7 +397,8 @@ int sensor_hub_input_get_attribute_info(struct hid_sensor_hub_device *hsdev, + for (i = 0; i < report->maxfield; ++i) { + field = report->field[i]; + if (field->maxusage) { +- if (field->physical == usage_id && ++ if ((field->physical == usage_id || ++ field->application == usage_id) && + (field->logical == attr_usage_id || + field->usage[0].hid == + attr_usage_id) && +@@ -506,7 +507,8 @@ static int sensor_hub_raw_event(struct hid_device *hdev, + collection->usage); + + callback = sensor_hub_get_callback(hdev, +- report->field[i]->physical, ++ report->field[i]->physical ?: ++ report->field[i]->application, + report->field[i]->usage[0].collection_index, + &hsdev, &priv); + if (!callback) { +-- +2.38.1 + diff --git a/1005-HID-core-Export-some-report-item-parsing-functions.patch b/1005-HID-core-Export-some-report-item-parsing-functions.patch new file mode 100644 index 0000000..05f1342 --- /dev/null +++ b/1005-HID-core-Export-some-report-item-parsing-functions.patch @@ -0,0 +1,221 @@ +From 360637979541fa2f3a64ed54af818f07e4e95a8d Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Ronald=20Tschal=C3=A4r?= +Date: Sat, 27 Feb 2021 17:26:41 -0800 +Subject: [PATCH 2/6] HID: core: Export some report item parsing functions. +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +These are useful to drivers that need to scan or parse reports +themselves. + +Signed-off-by: Ronald Tschalär +--- + drivers/hid/hid-core.c | 54 +++++++++++++++++++++++++----------------- + include/linux/hid.h | 4 ++++ + 2 files changed, 36 insertions(+), 22 deletions(-) + +diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c +index 8fcd663b10e2..f0d914238e55 100644 +--- a/drivers/hid/hid-core.c ++++ b/drivers/hid/hid-core.c +@@ -343,7 +343,7 @@ static int hid_add_field(struct hid_parser *parser, unsigned report_type, unsign + * Read data value from item. + */ + +-static u32 item_udata(struct hid_item *item) ++u32 hid_item_udata(struct hid_item *item) + { + switch (item->size) { + case 1: return item->data.u8; +@@ -352,8 +352,9 @@ static u32 item_udata(struct hid_item *item) + } + return 0; + } ++EXPORT_SYMBOL_GPL(hid_item_udata); + +-static s32 item_sdata(struct hid_item *item) ++s32 hid_item_sdata(struct hid_item *item) + { + switch (item->size) { + case 1: return item->data.s8; +@@ -362,6 +363,7 @@ static s32 item_sdata(struct hid_item *item) + } + return 0; + } ++EXPORT_SYMBOL_GPL(hid_item_sdata); + + /* + * Process a global item. +@@ -394,29 +396,29 @@ static int hid_parser_global(struct hid_parser *parser, struct hid_item *item) + return 0; + + case HID_GLOBAL_ITEM_TAG_USAGE_PAGE: +- parser->global.usage_page = item_udata(item); ++ parser->global.usage_page = hid_item_udata(item); + return 0; + + case HID_GLOBAL_ITEM_TAG_LOGICAL_MINIMUM: +- parser->global.logical_minimum = item_sdata(item); ++ parser->global.logical_minimum = hid_item_sdata(item); + return 0; + + case HID_GLOBAL_ITEM_TAG_LOGICAL_MAXIMUM: + if (parser->global.logical_minimum < 0) +- parser->global.logical_maximum = item_sdata(item); ++ parser->global.logical_maximum = hid_item_sdata(item); + else +- parser->global.logical_maximum = item_udata(item); ++ parser->global.logical_maximum = hid_item_udata(item); + return 0; + + case HID_GLOBAL_ITEM_TAG_PHYSICAL_MINIMUM: +- parser->global.physical_minimum = item_sdata(item); ++ parser->global.physical_minimum = hid_item_sdata(item); + return 0; + + case HID_GLOBAL_ITEM_TAG_PHYSICAL_MAXIMUM: + if (parser->global.physical_minimum < 0) +- parser->global.physical_maximum = item_sdata(item); ++ parser->global.physical_maximum = hid_item_sdata(item); + else +- parser->global.physical_maximum = item_udata(item); ++ parser->global.physical_maximum = hid_item_udata(item); + return 0; + + case HID_GLOBAL_ITEM_TAG_UNIT_EXPONENT: +@@ -424,7 +426,7 @@ static int hid_parser_global(struct hid_parser *parser, struct hid_item *item) + * nibble due to the common misunderstanding of HID + * specification 1.11, 6.2.2.7 Global Items. Attempt to handle + * both this and the standard encoding. */ +- raw_value = item_sdata(item); ++ raw_value = hid_item_sdata(item); + if (!(raw_value & 0xfffffff0)) + parser->global.unit_exponent = hid_snto32(raw_value, 4); + else +@@ -432,11 +434,11 @@ static int hid_parser_global(struct hid_parser *parser, struct hid_item *item) + return 0; + + case HID_GLOBAL_ITEM_TAG_UNIT: +- parser->global.unit = item_udata(item); ++ parser->global.unit = hid_item_udata(item); + return 0; + + case HID_GLOBAL_ITEM_TAG_REPORT_SIZE: +- parser->global.report_size = item_udata(item); ++ parser->global.report_size = hid_item_udata(item); + if (parser->global.report_size > 256) { + hid_err(parser->device, "invalid report_size %d\n", + parser->global.report_size); +@@ -445,7 +447,7 @@ static int hid_parser_global(struct hid_parser *parser, struct hid_item *item) + return 0; + + case HID_GLOBAL_ITEM_TAG_REPORT_COUNT: +- parser->global.report_count = item_udata(item); ++ parser->global.report_count = hid_item_udata(item); + if (parser->global.report_count > HID_MAX_USAGES) { + hid_err(parser->device, "invalid report_count %d\n", + parser->global.report_count); +@@ -454,7 +456,7 @@ static int hid_parser_global(struct hid_parser *parser, struct hid_item *item) + return 0; + + case HID_GLOBAL_ITEM_TAG_REPORT_ID: +- parser->global.report_id = item_udata(item); ++ parser->global.report_id = hid_item_udata(item); + if (parser->global.report_id == 0 || + parser->global.report_id >= HID_MAX_IDS) { + hid_err(parser->device, "report_id %u is invalid\n", +@@ -479,7 +481,7 @@ static int hid_parser_local(struct hid_parser *parser, struct hid_item *item) + unsigned n; + __u32 count; + +- data = item_udata(item); ++ data = hid_item_udata(item); + + switch (item->tag) { + case HID_LOCAL_ITEM_TAG_DELIMITER: +@@ -611,7 +613,7 @@ static int hid_parser_main(struct hid_parser *parser, struct hid_item *item) + + hid_concatenate_last_usage_page(parser); + +- data = item_udata(item); ++ data = hid_item_udata(item); + + switch (item->tag) { + case HID_MAIN_ITEM_TAG_BEGIN_COLLECTION: +@@ -712,12 +714,19 @@ static void hid_device_release(struct device *dev) + kfree(hid); + } + +-/* ++/** ++ * hid_fetch_item - fetch an item from a report ++ * ++ * @start: the current position in the report buffer to read the next item from ++ * @end: the end of the report buffer ++ * @item: the item struct to fill in ++ * + * Fetch a report description item from the data stream. We support long + * items, though they are not used yet. ++ * ++ * Return: the position of the next item in the report + */ +- +-static u8 *fetch_item(__u8 *start, __u8 *end, struct hid_item *item) ++u8 *hid_fetch_item(__u8 *start, __u8 *end, struct hid_item *item) + { + u8 b; + +@@ -778,6 +787,7 @@ static u8 *fetch_item(__u8 *start, __u8 *end, struct hid_item *item) + + return NULL; + } ++EXPORT_SYMBOL_GPL(hid_fetch_item); + + static void hid_scan_input_usage(struct hid_parser *parser, u32 usage) + { +@@ -836,7 +846,7 @@ static int hid_scan_main(struct hid_parser *parser, struct hid_item *item) + + hid_concatenate_last_usage_page(parser); + +- data = item_udata(item); ++ data = hid_item_udata(item); + + switch (item->tag) { + case HID_MAIN_ITEM_TAG_BEGIN_COLLECTION: +@@ -896,7 +906,7 @@ static int hid_scan_report(struct hid_device *hid) + * be robust against hid errors. Those errors will be raised by + * hid_open_report() anyway. + */ +- while ((start = fetch_item(start, end, &item)) != NULL) ++ while ((start = hid_fetch_item(start, end, &item)) != NULL) + dispatch_type[item.type](parser, &item); + + /* +@@ -1255,7 +1265,7 @@ int hid_open_report(struct hid_device *device) + device->collection_size = HID_DEFAULT_NUM_COLLECTIONS; + + ret = -EINVAL; +- while ((next = fetch_item(start, end, &item)) != NULL) { ++ while ((next = hid_fetch_item(start, end, &item)) != NULL) { + start = next; + + if (item.format != HID_ITEM_FORMAT_SHORT) { +diff --git a/include/linux/hid.h b/include/linux/hid.h +index 4363a63b9775..b39f97d565e2 100644 +--- a/include/linux/hid.h ++++ b/include/linux/hid.h +@@ -941,6 +941,10 @@ struct hid_report *hid_validate_values(struct hid_device *hid, + unsigned int field_index, + unsigned int report_counts); + ++u32 hid_item_udata(struct hid_item *item); ++s32 hid_item_sdata(struct hid_item *item); ++u8 *hid_fetch_item(__u8 *start, __u8 *end, struct hid_item *item); ++ + void hid_setup_resolution_multiplier(struct hid_device *hid); + int hid_open_report(struct hid_device *device); + int hid_check_keys_pressed(struct hid_device *hid); +-- +2.38.1 + diff --git a/1006-HID-apple-ibridge-Add-Apple-iBridge-HID-driver-for-T.patch b/1006-HID-apple-ibridge-Add-Apple-iBridge-HID-driver-for-T.patch new file mode 100644 index 0000000..c66d1bf --- /dev/null +++ b/1006-HID-apple-ibridge-Add-Apple-iBridge-HID-driver-for-T.patch @@ -0,0 +1,819 @@ +From cdc15881c4b8e31d3e39b73057ca211597d904cb Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Ronald=20Tschal=C3=A4r?= +Date: Sat, 27 Feb 2021 17:26:42 -0800 +Subject: [PATCH 3/6] HID: apple-ibridge: Add Apple iBridge HID driver for T1 + chip. +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +The iBridge device provides access to several devices, including: +- the Touch Bar +- the iSight webcam +- the light sensor +- the fingerprint sensor + +This driver provides the core support for managing the iBridge device +and the access to the underlying devices. In particular, the +functionality for the touch bar and light sensor is exposed via USB HID +interfaces, and on devices with the T1 chip one of the HID devices is +used for both functions. So this driver creates virtual HID devices, one +per top-level report collection on each HID device (for a total of 3 +virtual HID devices). The sub-drivers then bind to these virtual HID +devices. + +This way the Touch Bar and ALS drivers can be kept in their own modules, +while at the same time making them look very much like as if they were +connected to the real HID devices. And those drivers then work (mostly) +without further changes on MacBooks with the T2 chip that don't need +this driver. + +Signed-off-by: Ronald Tschalär +--- + drivers/hid/Kconfig | 16 + + drivers/hid/Makefile | 1 + + drivers/hid/apple-ibridge.c | 682 ++++++++++++++++++++++++++++++++++++ + drivers/hid/apple-ibridge.h | 15 + + drivers/hid/hid-ids.h | 1 + + drivers/hid/hid-quirks.c | 3 + + 6 files changed, 718 insertions(+) + create mode 100644 drivers/hid/apple-ibridge.c + create mode 100644 drivers/hid/apple-ibridge.h + +diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig +index c4308d4988dc..133be168691d 100644 +--- a/drivers/hid/Kconfig ++++ b/drivers/hid/Kconfig +@@ -138,6 +138,22 @@ config HID_APPLE + Say Y here if you want support for keyboards of Apple iBooks, PowerBooks, + MacBooks, MacBook Pros and Apple Aluminum. + ++config HID_APPLE_IBRIDGE ++ tristate "Apple iBridge" ++ depends on ACPI ++ depends on USB_HID ++ depends on X86 || COMPILE_TEST ++ imply HID_SENSOR_HUB ++ imply HID_SENSOR_ALS ++ help ++ This module provides the core support for the Apple T1 chip found ++ on 2016 and 2017 MacBookPro's, also known as the iBridge. The drivers ++ for the Touch Bar (apple-touchbar) and light sensor (hid-sensor-hub ++ and hid-sensor-als) need to be enabled separately. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called apple-ibridge. ++ + config HID_APPLEIR + tristate "Apple infrared receiver" + depends on (USB_HID) +diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile +index bccaec0d77d3..5a7dd0393df4 100644 +--- a/drivers/hid/Makefile ++++ b/drivers/hid/Makefile +@@ -26,6 +26,7 @@ obj-$(CONFIG_HID_ACCUTOUCH) += hid-accutouch.o + obj-$(CONFIG_HID_ALPS) += hid-alps.o + obj-$(CONFIG_HID_ACRUX) += hid-axff.o + obj-$(CONFIG_HID_APPLE) += hid-apple.o ++obj-$(CONFIG_HID_APPLE_IBRIDGE) += apple-ibridge.o + obj-$(CONFIG_HID_APPLEIR) += hid-appleir.o + obj-$(CONFIG_HID_CREATIVE_SB0540) += hid-creative-sb0540.o + obj-$(CONFIG_HID_ASUS) += hid-asus.o +diff --git a/drivers/hid/apple-ibridge.c b/drivers/hid/apple-ibridge.c +new file mode 100644 +index 000000000000..5f2b71c19974 +--- /dev/null ++++ b/drivers/hid/apple-ibridge.c +@@ -0,0 +1,682 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Apple iBridge Driver ++ * ++ * Copyright (c) 2018 Ronald Tschalär ++ */ ++ ++/** ++ * DOC: Overview ++ * ++ * 2016 and 2017 MacBookPro models with a Touch Bar (MacBookPro13,[23] and ++ * MacBookPro14,[23]) have an Apple iBridge chip (also known as T1 chip) which ++ * exposes the touch bar, built-in webcam (iSight), ambient light sensor, and ++ * Secure Enclave Processor (SEP) for TouchID. It shows up in the system as a ++ * USB device with 3 configurations: 'Default iBridge Interfaces', 'Default ++ * iBridge Interfaces(OS X)', and 'Default iBridge Interfaces(Recovery)'. ++ * ++ * In the first (default after boot) configuration, 4 usb interfaces are ++ * exposed: 2 related to the webcam, and 2 USB HID interfaces representing ++ * the touch bar and the ambient light sensor. The webcam interfaces are ++ * already handled by the uvcvideo driver. However, there is a problem with ++ * the other two interfaces: one of them contains functionality (HID reports) ++ * used by both the touch bar and the ALS, which is an issue because the kernel ++ * allows only one driver to be attached to a given device. This driver exists ++ * to solve this issue. ++ * ++ * This driver is implemented as a HID driver that attaches to both HID ++ * interfaces and in turn creates several virtual child HID devices, one for ++ * each top-level collection found in each interfaces report descriptor. The ++ * touch bar and ALS drivers then attach to these virtual HID devices, and this ++ * driver forwards the operations between the real and virtual devices. ++ * ++ * One important aspect of this approach is that resulting (virtual) HID ++ * devices look much like the HID devices found on the later MacBookPro models ++ * which have a T2 chip, where there are separate USB interfaces for the touch ++ * bar and ALS functionality, which means that the touch bar and ALS drivers ++ * work (mostly) the same on both types of models. ++ * ++ * Lastly, this driver also takes care of the power-management for the ++ * iBridge when suspending and resuming. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "hid-ids.h" ++#include "../hid/usbhid/usbhid.h" ++#include "apple-ibridge.h" ++ ++#define APPLEIB_BASIC_CONFIG 1 ++ ++#define LOG_DEV(ib_dev) (&(ib_dev)->acpi_dev->dev) ++ ++static struct hid_device_id appleib_sub_hid_ids[] = { ++ { HID_USB_DEVICE(USB_VENDOR_ID_LINUX_FOUNDATION, ++ USB_DEVICE_ID_IBRIDGE_TB) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_LINUX_FOUNDATION, ++ USB_DEVICE_ID_IBRIDGE_ALS) }, ++}; ++ ++static struct { ++ unsigned int usage; ++ struct hid_device_id *dev_id; ++} appleib_usage_map[] = { ++ /* Default iBridge configuration, key inputs and mode settings */ ++ { 0x00010006, &appleib_sub_hid_ids[0] }, ++ /* OS X iBridge configuration, digitizer inputs */ ++ { 0x000D0005, &appleib_sub_hid_ids[0] }, ++ /* All iBridge configurations, display/DFR settings */ ++ { 0xFF120001, &appleib_sub_hid_ids[0] }, ++ /* All iBridge configurations, ALS */ ++ { 0x00200041, &appleib_sub_hid_ids[1] }, ++}; ++ ++struct appleib_device { ++ struct acpi_device *acpi_dev; ++ acpi_handle asoc_socw; ++}; ++ ++struct appleib_hid_dev_info { ++ struct hid_device *hdev; ++ struct hid_device *sub_hdevs[ARRAY_SIZE(appleib_sub_hid_ids)]; ++ bool sub_open[ARRAY_SIZE(appleib_sub_hid_ids)]; ++}; ++ ++static int appleib_hid_raw_event(struct hid_device *hdev, ++ struct hid_report *report, u8 *data, int size) ++{ ++ struct appleib_hid_dev_info *hdev_info = hid_get_drvdata(hdev); ++ int i; ++ ++ for (i = 0; i < ARRAY_SIZE(hdev_info->sub_hdevs); i++) { ++ if (READ_ONCE(hdev_info->sub_open[i])) ++ hid_input_report(hdev_info->sub_hdevs[i], report->type, ++ data, size, 0); ++ } ++ ++ return 0; ++} ++ ++static __u8 *appleib_report_fixup(struct hid_device *hdev, __u8 *rdesc, ++ unsigned int *rsize) ++{ ++ /* Some fields have a size of 64 bits, which according to HID 1.11 ++ * Section 8.4 is not valid ("An item field cannot span more than 4 ++ * bytes in a report"). Furthermore, hid_field_extract() complains ++ * when encountering such a field. So turn them into two 32-bit fields ++ * instead. ++ */ ++ ++ if (*rsize == 634 && ++ /* Usage Page 0xff12 (vendor defined) */ ++ rdesc[212] == 0x06 && rdesc[213] == 0x12 && rdesc[214] == 0xff && ++ /* Usage 0x51 */ ++ rdesc[416] == 0x09 && rdesc[417] == 0x51 && ++ /* report size 64 */ ++ rdesc[432] == 0x75 && rdesc[433] == 64 && ++ /* report count 1 */ ++ rdesc[434] == 0x95 && rdesc[435] == 1) { ++ rdesc[433] = 32; ++ rdesc[435] = 2; ++ hid_dbg(hdev, "Fixed up first 64-bit field\n"); ++ } ++ ++ if (*rsize == 634 && ++ /* Usage Page 0xff12 (vendor defined) */ ++ rdesc[212] == 0x06 && rdesc[213] == 0x12 && rdesc[214] == 0xff && ++ /* Usage 0x51 */ ++ rdesc[611] == 0x09 && rdesc[612] == 0x51 && ++ /* report size 64 */ ++ rdesc[627] == 0x75 && rdesc[628] == 64 && ++ /* report count 1 */ ++ rdesc[629] == 0x95 && rdesc[630] == 1) { ++ rdesc[628] = 32; ++ rdesc[630] = 2; ++ hid_dbg(hdev, "Fixed up second 64-bit field\n"); ++ } ++ ++ return rdesc; ++} ++ ++#ifdef CONFIG_PM ++/** ++ * appleib_forward_int_op() - Forward a hid-driver callback to all drivers on ++ * all virtual HID devices attached to the given real HID device. ++ * @hdev the real hid-device ++ * @forward a function that calls the callback on the given driver ++ * @args arguments for the forward function ++ * ++ * This is for callbacks that return a status as an int. ++ * ++ * Returns: 0 on success, or the first error returned by the @forward function. ++ */ ++static int appleib_forward_int_op(struct hid_device *hdev, ++ int (*forward)(struct hid_driver *, ++ struct hid_device *, void *), ++ void *args) ++{ ++ struct appleib_hid_dev_info *hdev_info = hid_get_drvdata(hdev); ++ struct hid_device *sub_hdev; ++ int rc = 0; ++ int i; ++ ++ for (i = 0; i < ARRAY_SIZE(hdev_info->sub_hdevs); i++) { ++ sub_hdev = hdev_info->sub_hdevs[i]; ++ if (sub_hdev->driver) { ++ rc = forward(sub_hdev->driver, sub_hdev, args); ++ if (rc) ++ break; ++ } ++ ++ break; ++ } ++ ++ return rc; ++} ++ ++static int appleib_hid_suspend_fwd(struct hid_driver *drv, ++ struct hid_device *hdev, void *args) ++{ ++ int rc = 0; ++ ++ if (drv->suspend) ++ rc = drv->suspend(hdev, *(pm_message_t *)args); ++ ++ return rc; ++} ++ ++static int appleib_hid_suspend(struct hid_device *hdev, pm_message_t message) ++{ ++ return appleib_forward_int_op(hdev, appleib_hid_suspend_fwd, &message); ++} ++ ++static int appleib_hid_resume_fwd(struct hid_driver *drv, ++ struct hid_device *hdev, void *args) ++{ ++ int rc = 0; ++ ++ if (drv->resume) ++ rc = drv->resume(hdev); ++ ++ return rc; ++} ++ ++static int appleib_hid_resume(struct hid_device *hdev) ++{ ++ return appleib_forward_int_op(hdev, appleib_hid_resume_fwd, NULL); ++} ++ ++static int appleib_hid_reset_resume_fwd(struct hid_driver *drv, ++ struct hid_device *hdev, void *args) ++{ ++ int rc = 0; ++ ++ if (drv->reset_resume) ++ rc = drv->reset_resume(hdev); ++ ++ return rc; ++} ++ ++static int appleib_hid_reset_resume(struct hid_device *hdev) ++{ ++ return appleib_forward_int_op(hdev, appleib_hid_reset_resume_fwd, NULL); ++} ++#endif /* CONFIG_PM */ ++ ++static int appleib_ll_start(struct hid_device *hdev) ++{ ++ return 0; ++} ++ ++static void appleib_ll_stop(struct hid_device *hdev) ++{ ++} ++ ++static int appleib_set_open(struct hid_device *hdev, bool open) ++{ ++ struct appleib_hid_dev_info *hdev_info = hdev->driver_data; ++ int i; ++ ++ for (i = 0; i < ARRAY_SIZE(hdev_info->sub_hdevs); i++) { ++ /* ++ * hid_hw_open(), and hence appleib_ll_open(), is called ++ * from the driver's probe function, which in turn is called ++ * while adding the sub-hdev; but at this point we haven't yet ++ * added the sub-hdev to our list. So if we don't find the ++ * sub-hdev in our list assume it's in the process of being ++ * added and set the flag on the first unset sub-hdev. ++ */ ++ if (hdev_info->sub_hdevs[i] == hdev || ++ !hdev_info->sub_hdevs[i]) { ++ WRITE_ONCE(hdev_info->sub_open[i], open); ++ return 0; ++ } ++ } ++ ++ return -ENODEV; ++} ++ ++static int appleib_ll_open(struct hid_device *hdev) ++{ ++ return appleib_set_open(hdev, true); ++} ++ ++static void appleib_ll_close(struct hid_device *hdev) ++{ ++ appleib_set_open(hdev, false); ++} ++ ++static int appleib_ll_power(struct hid_device *hdev, int level) ++{ ++ struct appleib_hid_dev_info *hdev_info = hdev->driver_data; ++ ++ return hid_hw_power(hdev_info->hdev, level); ++} ++ ++static int appleib_ll_parse(struct hid_device *hdev) ++{ ++ /* we've already called hid_parse_report() */ ++ return 0; ++} ++ ++static void appleib_ll_request(struct hid_device *hdev, ++ struct hid_report *report, int reqtype) ++{ ++ struct appleib_hid_dev_info *hdev_info = hdev->driver_data; ++ ++ hid_hw_request(hdev_info->hdev, report, reqtype); ++} ++ ++static int appleib_ll_wait(struct hid_device *hdev) ++{ ++ struct appleib_hid_dev_info *hdev_info = hdev->driver_data; ++ ++ hid_hw_wait(hdev_info->hdev); ++ return 0; ++} ++ ++static int appleib_ll_raw_request(struct hid_device *hdev, ++ unsigned char reportnum, __u8 *buf, ++ size_t len, unsigned char rtype, int reqtype) ++{ ++ struct appleib_hid_dev_info *hdev_info = hdev->driver_data; ++ ++ return hid_hw_raw_request(hdev_info->hdev, reportnum, buf, len, rtype, ++ reqtype); ++} ++ ++static int appleib_ll_output_report(struct hid_device *hdev, __u8 *buf, ++ size_t len) ++{ ++ struct appleib_hid_dev_info *hdev_info = hdev->driver_data; ++ ++ return hid_hw_output_report(hdev_info->hdev, buf, len); ++} ++ ++static struct hid_ll_driver appleib_ll_driver = { ++ .start = appleib_ll_start, ++ .stop = appleib_ll_stop, ++ .open = appleib_ll_open, ++ .close = appleib_ll_close, ++ .power = appleib_ll_power, ++ .parse = appleib_ll_parse, ++ .request = appleib_ll_request, ++ .wait = appleib_ll_wait, ++ .raw_request = appleib_ll_raw_request, ++ .output_report = appleib_ll_output_report, ++}; ++ ++static __u8 *appleib_find_collection(__u8 *start, __u8 *end, ++ unsigned int *usage) ++{ ++ struct hid_item item; ++ int depth = 0; ++ ++ *usage = 0; ++ ++ while ((start = hid_fetch_item(start, end, &item)) != NULL) { ++ if (item.type == HID_ITEM_TYPE_MAIN) { ++ switch (item.tag) { ++ case HID_MAIN_ITEM_TAG_BEGIN_COLLECTION: ++ depth++; ++ break; ++ ++ case HID_MAIN_ITEM_TAG_END_COLLECTION: ++ depth--; ++ if (depth <= 0) ++ return start; ++ break; ++ } ++ } else if (item.type == HID_ITEM_TYPE_GLOBAL && ++ item.tag == HID_GLOBAL_ITEM_TAG_USAGE_PAGE && ++ depth == 0) { ++ *usage = (*usage & 0x0000FFFF) | ++ ((hid_item_udata(&item) & 0xFFFF) << 16); ++ } else if (item.type == HID_ITEM_TYPE_LOCAL && ++ item.tag == HID_LOCAL_ITEM_TAG_USAGE && ++ depth == 0) { ++ *usage = (*usage & 0xFFFF0000) | ++ (hid_item_udata(&item) & 0xFFFF); ++ } ++ } ++ ++ return end; ++} ++ ++static struct hid_device_id *appleib_find_dev_id_for_usage(unsigned int usage) ++{ ++ int i; ++ ++ for (i = 0; i < ARRAY_SIZE(appleib_usage_map); i++) { ++ if (appleib_usage_map[i].usage == usage) ++ return appleib_usage_map[i].dev_id; ++ } ++ ++ return NULL; ++} ++ ++static struct hid_device * ++appleib_add_sub_dev(struct appleib_hid_dev_info *hdev_info, ++ struct hid_device_id *dev_id, u8 *rdesc, size_t rsize) ++{ ++ struct hid_device *sub_hdev; ++ int rc; ++ ++ sub_hdev = hid_allocate_device(); ++ if (IS_ERR(sub_hdev)) ++ return sub_hdev; ++ ++ sub_hdev->dev.parent = &hdev_info->hdev->dev; ++ ++ sub_hdev->bus = dev_id->bus; ++ sub_hdev->group = dev_id->group; ++ sub_hdev->vendor = dev_id->vendor; ++ sub_hdev->product = dev_id->product; ++ ++ sub_hdev->ll_driver = &appleib_ll_driver; ++ ++ snprintf(sub_hdev->name, sizeof(sub_hdev->name), ++ "iBridge Virtual HID %s/%04x:%04x", ++ dev_name(sub_hdev->dev.parent), sub_hdev->vendor, ++ sub_hdev->product); ++ ++ sub_hdev->driver_data = hdev_info; ++ ++ rc = hid_parse_report(sub_hdev, rdesc, rsize); ++ if (rc) { ++ hid_destroy_device(sub_hdev); ++ return ERR_PTR(rc); ++ } ++ ++ rc = hid_add_device(sub_hdev); ++ if (rc) { ++ hid_destroy_device(sub_hdev); ++ return ERR_PTR(rc); ++ } ++ ++ return sub_hdev; ++} ++ ++static struct appleib_hid_dev_info *appleib_add_device(struct hid_device *hdev) ++{ ++ struct appleib_hid_dev_info *hdev_info; ++ __u8 *start = hdev->dev_rdesc; ++ __u8 *end = start + hdev->dev_rsize; ++ __u8 *pos; ++ struct hid_device_id *dev_id; ++ unsigned int usage; ++ int i; ++ ++ hdev_info = devm_kzalloc(&hdev->dev, sizeof(*hdev_info), GFP_KERNEL); ++ if (!hdev_info) ++ return ERR_PTR(-ENOMEM); ++ ++ hdev_info->hdev = hdev; ++ ++ for (i = 0; ; ) { ++ pos = appleib_find_collection(start, end, &usage); ++ ++ dev_id = appleib_find_dev_id_for_usage(usage); ++ if (!dev_id) { ++ hid_warn(hdev, "Unknown collection encountered with usage %x\n", ++ usage); ++ ++ } else if (i >= ARRAY_SIZE(hdev_info->sub_hdevs)) { ++ hid_warn(hdev, "Too many collections encountered - ignoring for usage %x\n", ++ usage); ++ } else { ++ hdev_info->sub_hdevs[i] = ++ appleib_add_sub_dev(hdev_info, dev_id, start, ++ pos - start); ++ if (IS_ERR(hdev_info->sub_hdevs[i])) { ++ while (i-- > 0) ++ hid_destroy_device(hdev_info->sub_hdevs[i]); ++ return (void *)hdev_info->sub_hdevs[i]; ++ } ++ ++ i++; ++ } ++ ++ start = pos; ++ if (start >= end) ++ break; ++ } ++ ++ return hdev_info; ++} ++ ++static void appleib_remove_device(struct hid_device *hdev) ++{ ++ struct appleib_hid_dev_info *hdev_info = hid_get_drvdata(hdev); ++ int i; ++ ++ for (i = 0; i < ARRAY_SIZE(hdev_info->sub_hdevs); i++) { ++ if (hdev_info->sub_hdevs[i]) ++ hid_destroy_device(hdev_info->sub_hdevs[i]); ++ } ++ ++ hid_set_drvdata(hdev, NULL); ++} ++ ++static int appleib_hid_probe(struct hid_device *hdev, ++ const struct hid_device_id *id) ++{ ++ struct appleib_hid_dev_info *hdev_info; ++ struct usb_device *udev; ++ int rc; ++ ++ /* check and set usb config first */ ++ udev = hid_to_usb_dev(hdev); ++ ++ if (udev->actconfig->desc.bConfigurationValue != APPLEIB_BASIC_CONFIG) { ++ rc = usb_driver_set_configuration(udev, APPLEIB_BASIC_CONFIG); ++ return rc ? rc : -ENODEV; ++ } ++ ++ rc = hid_parse(hdev); ++ if (rc) { ++ hid_err(hdev, "ib: hid parse failed (%d)\n", rc); ++ goto error; ++ } ++ ++ rc = hid_hw_start(hdev, HID_CONNECT_DRIVER); ++ if (rc) { ++ hid_err(hdev, "ib: hw start failed (%d)\n", rc); ++ goto error; ++ } ++ ++ hdev_info = appleib_add_device(hdev); ++ if (IS_ERR(hdev_info)) { ++ rc = PTR_ERR(hdev_info); ++ goto stop_hw; ++ } ++ ++ hid_set_drvdata(hdev, hdev_info); ++ ++ rc = hid_hw_open(hdev); ++ if (rc) { ++ hid_err(hdev, "ib: failed to open hid: %d\n", rc); ++ goto remove_dev; ++ } ++ ++ return 0; ++ ++remove_dev: ++ appleib_remove_device(hdev); ++stop_hw: ++ hid_hw_stop(hdev); ++error: ++ return rc; ++} ++ ++static void appleib_hid_remove(struct hid_device *hdev) ++{ ++ hid_hw_close(hdev); ++ appleib_remove_device(hdev); ++ hid_hw_stop(hdev); ++} ++ ++static const struct hid_device_id appleib_hid_ids[] = { ++ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_IBRIDGE) }, ++ { }, ++}; ++ ++static struct hid_driver appleib_hid_driver = { ++ .name = "apple-ibridge-hid", ++ .id_table = appleib_hid_ids, ++ .probe = appleib_hid_probe, ++ .remove = appleib_hid_remove, ++ .raw_event = appleib_hid_raw_event, ++ .report_fixup = appleib_report_fixup, ++#ifdef CONFIG_PM ++ .suspend = appleib_hid_suspend, ++ .resume = appleib_hid_resume, ++ .reset_resume = appleib_hid_reset_resume, ++#endif ++}; ++ ++static struct appleib_device *appleib_alloc_device(struct acpi_device *acpi_dev) ++{ ++ struct appleib_device *ib_dev; ++ acpi_status sts; ++ ++ ib_dev = devm_kzalloc(&acpi_dev->dev, sizeof(*ib_dev), GFP_KERNEL); ++ if (!ib_dev) ++ return ERR_PTR(-ENOMEM); ++ ++ ib_dev->acpi_dev = acpi_dev; ++ ++ /* get iBridge acpi power control method for suspend/resume */ ++ sts = acpi_get_handle(acpi_dev->handle, "SOCW", &ib_dev->asoc_socw); ++ if (ACPI_FAILURE(sts)) { ++ dev_err(LOG_DEV(ib_dev), ++ "Error getting handle for ASOC.SOCW method: %s\n", ++ acpi_format_exception(sts)); ++ return ERR_PTR(-ENXIO); ++ } ++ ++ /* ensure iBridge is powered on */ ++ sts = acpi_execute_simple_method(ib_dev->asoc_socw, NULL, 1); ++ if (ACPI_FAILURE(sts)) ++ dev_warn(LOG_DEV(ib_dev), "SOCW(1) failed: %s\n", ++ acpi_format_exception(sts)); ++ ++ return ib_dev; ++} ++ ++static int appleib_probe(struct acpi_device *acpi) ++{ ++ struct appleib_device *ib_dev; ++ int ret; ++ ++ ib_dev = appleib_alloc_device(acpi); ++ if (IS_ERR(ib_dev)) ++ return PTR_ERR(ib_dev); ++ ++ ret = hid_register_driver(&appleib_hid_driver); ++ if (ret) { ++ dev_err(LOG_DEV(ib_dev), "Error registering hid driver: %d\n", ++ ret); ++ return ret; ++ } ++ ++ acpi->driver_data = ib_dev; ++ ++ return 0; ++} ++ ++static int appleib_remove(struct acpi_device *acpi) ++{ ++ hid_unregister_driver(&appleib_hid_driver); ++ ++ return 0; ++} ++ ++static int appleib_suspend(struct device *dev) ++{ ++ struct appleib_device *ib_dev; ++ int rc; ++ ++ ib_dev = acpi_driver_data(to_acpi_device(dev)); ++ ++ rc = acpi_execute_simple_method(ib_dev->asoc_socw, NULL, 0); ++ if (ACPI_FAILURE(rc)) ++ dev_warn(dev, "SOCW(0) failed: %s\n", ++ acpi_format_exception(rc)); ++ ++ return 0; ++} ++ ++static int appleib_resume(struct device *dev) ++{ ++ struct appleib_device *ib_dev; ++ int rc; ++ ++ ib_dev = acpi_driver_data(to_acpi_device(dev)); ++ ++ rc = acpi_execute_simple_method(ib_dev->asoc_socw, NULL, 1); ++ if (ACPI_FAILURE(rc)) ++ dev_warn(dev, "SOCW(1) failed: %s\n", ++ acpi_format_exception(rc)); ++ ++ return 0; ++} ++ ++static const struct dev_pm_ops appleib_pm = { ++ .suspend = appleib_suspend, ++ .resume = appleib_resume, ++ .restore = appleib_resume, ++}; ++ ++static const struct acpi_device_id appleib_acpi_match[] = { ++ { "APP7777", 0 }, ++ { }, ++}; ++ ++MODULE_DEVICE_TABLE(acpi, appleib_acpi_match); ++ ++static struct acpi_driver appleib_driver = { ++ .name = "apple-ibridge", ++ .class = "apple_ibridge", ++ .owner = THIS_MODULE, ++ .ids = appleib_acpi_match, ++ .ops = { ++ .add = appleib_probe, ++ .remove = appleib_remove, ++ }, ++ .drv = { ++ .pm = &appleib_pm, ++ }, ++}; ++ ++module_acpi_driver(appleib_driver) ++ ++MODULE_AUTHOR("Ronald Tschalär"); ++MODULE_DESCRIPTION("Apple iBridge driver"); ++MODULE_LICENSE("GPL v2"); +diff --git a/drivers/hid/apple-ibridge.h b/drivers/hid/apple-ibridge.h +new file mode 100644 +index 000000000000..8aefcf61589a +--- /dev/null ++++ b/drivers/hid/apple-ibridge.h +@@ -0,0 +1,15 @@ ++/* SPDX-License-Identifier: GPL-2.0 */ ++/* ++ * Apple iBridge Driver ++ * ++ * Copyright (c) 2018 Ronald Tschalär ++ */ ++ ++#ifndef __LINUX_APPLE_IBRDIGE_H ++#define __LINUX_APPLE_IBRDIGE_H ++ ++#define USB_VENDOR_ID_LINUX_FOUNDATION 0x1d6b ++#define USB_DEVICE_ID_IBRIDGE_TB 0x0301 ++#define USB_DEVICE_ID_IBRIDGE_ALS 0x0302 ++ ++#endif +diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h +index 043cf1cc8794..bc92fa5f8573 100644 +--- a/drivers/hid/hid-ids.h ++++ b/drivers/hid/hid-ids.h +@@ -187,6 +187,7 @@ + #define USB_DEVICE_ID_APPLE_MAGIC_KEYBOARD_NUMPAD_2021 0x029f + #define USB_DEVICE_ID_APPLE_TOUCHBAR_BACKLIGHT 0x8102 + #define USB_DEVICE_ID_APPLE_TOUCHBAR_DISPLAY 0x8302 ++#define USB_DEVICE_ID_APPLE_IBRIDGE 0x8600 + + #define USB_VENDOR_ID_ASUS 0x0486 + #define USB_DEVICE_ID_ASUS_T91MT 0x0185 +diff --git a/drivers/hid/hid-quirks.c b/drivers/hid/hid-quirks.c +index 70f602c64fd1..cc862fbf1995 100644 +--- a/drivers/hid/hid-quirks.c ++++ b/drivers/hid/hid-quirks.c +@@ -317,6 +317,9 @@ static const struct hid_device_id hid_have_special_driver[] = { + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_TOUCHBAR_BACKLIGHT) }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_TOUCHBAR_DISPLAY) }, + #endif ++#if IS_ENABLED(CONFIG_HID_APPLE_IBRIDGE) ++ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_IBRIDGE) }, ++#endif + #if IS_ENABLED(CONFIG_HID_APPLEIR) + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_IRCONTROL) }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_IRCONTROL2) }, +-- +2.38.1 + diff --git a/1007-HID-apple-touchbar-Add-driver-for-the-Touch-Bar-on-M.patch b/1007-HID-apple-touchbar-Add-driver-for-the-Touch-Bar-on-M.patch new file mode 100644 index 0000000..11d220e --- /dev/null +++ b/1007-HID-apple-touchbar-Add-driver-for-the-Touch-Bar-on-M.patch @@ -0,0 +1,1593 @@ +From 4391ae4286a2ab1c7ec1aae430338b0a683f6404 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Ronald=20Tschal=C3=A4r?= +Date: Sat, 27 Feb 2021 17:26:43 -0800 +Subject: [PATCH 4/6] HID: apple-touchbar - Add driver for the Touch Bar on + MacBook Pro's. +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +This driver enables basic touch bar functionality: enabling it, switching +between modes on FN key press, and dimming and turning the display +off/on when idle/active. + +Signed-off-by: Ronald Tschalär +--- + drivers/hid/Kconfig | 10 + + drivers/hid/Makefile | 1 + + drivers/hid/apple-touchbar.c | 1523 ++++++++++++++++++++++++++++++++++ + 3 files changed, 1534 insertions(+) + create mode 100644 drivers/hid/apple-touchbar.c + +diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig +index 133be168691d..d5a61773a163 100644 +--- a/drivers/hid/Kconfig ++++ b/drivers/hid/Kconfig +@@ -143,6 +143,7 @@ config HID_APPLE_IBRIDGE + depends on ACPI + depends on USB_HID + depends on X86 || COMPILE_TEST ++ imply HID_APPLE_TOUCHBAR + imply HID_SENSOR_HUB + imply HID_SENSOR_ALS + help +@@ -154,6 +155,15 @@ config HID_APPLE_IBRIDGE + To compile this driver as a module, choose M here: the + module will be called apple-ibridge. + ++config HID_APPLE_TOUCHBAR ++ tristate "Apple Touch Bar" ++ help ++ Say Y here if you want support for the Touch Bar on recent ++ MacBook Pros. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called apple-touchbar. ++ + config HID_APPLEIR + tristate "Apple infrared receiver" + depends on (USB_HID) +diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile +index 5a7dd0393df4..3f0706a459ee 100644 +--- a/drivers/hid/Makefile ++++ b/drivers/hid/Makefile +@@ -27,6 +27,7 @@ obj-$(CONFIG_HID_ALPS) += hid-alps.o + obj-$(CONFIG_HID_ACRUX) += hid-axff.o + obj-$(CONFIG_HID_APPLE) += hid-apple.o + obj-$(CONFIG_HID_APPLE_IBRIDGE) += apple-ibridge.o ++obj-$(CONFIG_HID_APPLE_TOUCHBAR) += apple-touchbar.o + obj-$(CONFIG_HID_APPLEIR) += hid-appleir.o + obj-$(CONFIG_HID_CREATIVE_SB0540) += hid-creative-sb0540.o + obj-$(CONFIG_HID_ASUS) += hid-asus.o +diff --git a/drivers/hid/apple-touchbar.c b/drivers/hid/apple-touchbar.c +new file mode 100644 +index 000000000000..87cb9ebafb61 +--- /dev/null ++++ b/drivers/hid/apple-touchbar.c +@@ -0,0 +1,1523 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Apple Touch Bar Driver ++ * ++ * Copyright (c) 2017-2018 Ronald Tschalär ++ */ ++ ++/* ++ * Recent MacBookPro models (MacBookPro 13,[23] and later) have a touch bar, ++ * which is exposed via several USB interfaces. MacOS supports a fancy mode ++ * where arbitrary buttons can be defined; this driver currently only ++ * supports the simple mode that consists of 3 predefined layouts ++ * (escape-only, esc + special keys, and esc + function keys). ++ * ++ * The first USB HID interface supports two reports, an input report that ++ * is used to report the key presses, and an output report which can be ++ * used to set the touch bar "mode": touch bar off (in which case no touches ++ * are reported at all), escape key only, escape + 12 function keys, and ++ * escape + several special keys (including brightness, audio volume, ++ * etc). The second interface supports several, complex reports, most of ++ * which are unknown at this time, but one of which has been determined to ++ * allow for controlling of the touch bar's brightness: off (though touches ++ * are still reported), dimmed, and full brightness. This driver makes ++ * use of these two reports. ++ */ ++ ++#define dev_fmt(fmt) "tb: " fmt ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "hid-ids.h" ++#include "apple-ibridge.h" ++ ++#define HID_UP_APPLE 0xff120000 ++#define HID_USAGE_MODE (HID_UP_CUSTOM | 0x0004) ++#define HID_USAGE_APPLE_APP (HID_UP_APPLE | 0x0001) ++#define HID_USAGE_DISP (HID_UP_APPLE | 0x0021) ++#define HID_USAGE_DISP_AUX1 (HID_UP_APPLE | 0x0020) ++ ++#define APPLETB_MAX_TB_KEYS 13 /* ESC, F1-F12 */ ++ ++#define APPLETB_CMD_MODE_ESC 0 ++#define APPLETB_CMD_MODE_FN 1 ++#define APPLETB_CMD_MODE_SPCL 2 ++#define APPLETB_CMD_MODE_OFF 3 ++#define APPLETB_CMD_MODE_UPD 254 ++#define APPLETB_CMD_MODE_NONE 255 ++ ++#define APPLETB_CMD_DISP_ON 1 ++#define APPLETB_CMD_DISP_DIM 2 ++#define APPLETB_CMD_DISP_OFF 4 ++#define APPLETB_CMD_DISP_UPD 254 ++#define APPLETB_CMD_DISP_NONE 255 ++ ++#define APPLETB_FN_MODE_FKEYS 0 ++#define APPLETB_FN_MODE_NORM 1 ++#define APPLETB_FN_MODE_INV 2 ++#define APPLETB_FN_MODE_SPCL 3 ++#define APPLETB_FN_MODE_ESC 4 ++#define APPLETB_FN_MODE_MAX APPLETB_FN_MODE_ESC ++ ++#define APPLETB_DEVID_KEYBOARD 1 ++#define APPLETB_DEVID_TOUCHPAD 2 ++ ++#define APPLETB_MAX_DIM_TIME 30 ++ ++#define APPLETB_FEATURE_IS_T1 BIT(0) ++ ++static int appletb_tb_def_idle_timeout = 5 * 60; ++module_param_named(idle_timeout, appletb_tb_def_idle_timeout, int, 0444); ++MODULE_PARM_DESC(idle_timeout, "Default touch bar idle timeout:\n" ++ " [>0] - turn touch bar display off after no keyboard, trackpad, or touch bar input has been received for this many seconds;\n" ++ " the display will be turned back on as soon as new input is received\n" ++ " 0 - turn touch bar display off (input does not turn it on again)\n" ++ " -1 - turn touch bar display on (does not turn off automatically)\n" ++ " -2 - disable touch bar completely"); ++ ++static int appletb_tb_def_dim_timeout = -2; ++module_param_named(dim_timeout, appletb_tb_def_dim_timeout, int, 0444); ++MODULE_PARM_DESC(dim_timeout, "Default touch bar dim timeout:\n" ++ " >0 - dim touch bar display after no keyboard, trackpad, or touch bar input has been received for this many seconds\n" ++ " the display will be returned to full brightness as soon as new input is received\n" ++ " 0 - dim touch bar display (input does not return it to full brightness)\n" ++ " -1 - disable timeout (touch bar never dimmed)\n" ++ " [-2] - calculate timeout based on idle-timeout"); ++ ++static int appletb_tb_def_fn_mode = APPLETB_FN_MODE_NORM; ++module_param_named(fnmode, appletb_tb_def_fn_mode, int, 0444); ++MODULE_PARM_DESC(fnmode, "Default Fn key mode:\n" ++ " 0 - function-keys only\n" ++ " [1] - fn key switches from special to function-keys\n" ++ " 2 - inverse of 1\n" ++ " 3 - special keys only\n" ++ " 4 - escape key only"); ++ ++static ssize_t idle_timeout_show(struct device *dev, ++ struct device_attribute *attr, char *buf); ++static ssize_t idle_timeout_store(struct device *dev, ++ struct device_attribute *attr, ++ const char *buf, size_t size); ++static DEVICE_ATTR_RW(idle_timeout); ++ ++static ssize_t dim_timeout_show(struct device *dev, ++ struct device_attribute *attr, char *buf); ++static ssize_t dim_timeout_store(struct device *dev, ++ struct device_attribute *attr, ++ const char *buf, size_t size); ++static DEVICE_ATTR_RW(dim_timeout); ++ ++static ssize_t fnmode_show(struct device *dev, struct device_attribute *attr, ++ char *buf); ++static ssize_t fnmode_store(struct device *dev, struct device_attribute *attr, ++ const char *buf, size_t size); ++static DEVICE_ATTR_RW(fnmode); ++ ++static struct attribute *appletb_attrs[] = { ++ &dev_attr_idle_timeout.attr, ++ &dev_attr_dim_timeout.attr, ++ &dev_attr_fnmode.attr, ++ NULL, ++}; ++ ++static const struct attribute_group appletb_attr_group = { ++ .attrs = appletb_attrs, ++}; ++ ++struct appletb_device { ++ bool active; ++ struct device *log_dev; ++ ++ struct hid_field *mode_field; ++ struct hid_field *disp_field; ++ struct hid_field *disp_field_aux1; ++ struct appletb_iface_info { ++ struct hid_device *hdev; ++ struct usb_interface *usb_iface; ++ bool suspended; ++ } mode_iface, disp_iface; ++ ++ struct input_handler inp_handler; ++ struct input_handle kbd_handle; ++ struct input_handle tpd_handle; ++ ++ bool last_tb_keys_pressed[APPLETB_MAX_TB_KEYS]; ++ bool last_tb_keys_translated[APPLETB_MAX_TB_KEYS]; ++ bool last_fn_pressed; ++ ++ ktime_t last_event_time; ++ ++ unsigned char cur_tb_mode; ++ unsigned char pnd_tb_mode; ++ unsigned char cur_tb_disp; ++ unsigned char pnd_tb_disp; ++ bool tb_autopm_off; ++ bool restore_autopm; ++ struct delayed_work tb_work; ++ /* protects most of the above */ ++ spinlock_t tb_lock; ++ ++ int dim_timeout; ++ int idle_timeout; ++ bool dim_to_is_calc; ++ int fn_mode; ++ ++ bool is_t1; ++}; ++ ++struct appletb_key_translation { ++ u16 from; ++ u16 to; ++}; ++ ++static const struct appletb_key_translation appletb_fn_codes[] = { ++ { KEY_F1, KEY_BRIGHTNESSDOWN }, ++ { KEY_F2, KEY_BRIGHTNESSUP }, ++ { KEY_F3, KEY_SCALE }, /* not used */ ++ { KEY_F4, KEY_DASHBOARD }, /* not used */ ++ { KEY_F5, KEY_KBDILLUMDOWN }, ++ { KEY_F6, KEY_KBDILLUMUP }, ++ { KEY_F7, KEY_PREVIOUSSONG }, ++ { KEY_F8, KEY_PLAYPAUSE }, ++ { KEY_F9, KEY_NEXTSONG }, ++ { KEY_F10, KEY_MUTE }, ++ { KEY_F11, KEY_VOLUMEDOWN }, ++ { KEY_F12, KEY_VOLUMEUP }, ++}; ++ ++static struct appletb_device *appletb_dev; ++ ++static int appletb_send_usb_ctrl(struct appletb_iface_info *iface_info, ++ __u8 requesttype, struct hid_report *report, ++ void *data, __u16 size) ++{ ++ struct usb_device *dev = interface_to_usbdev(iface_info->usb_iface); ++ u8 ifnum = iface_info->usb_iface->cur_altsetting->desc.bInterfaceNumber; ++ int tries = 0; ++ int rc; ++ ++ do { ++ rc = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), ++ HID_REQ_SET_REPORT, requesttype, ++ (report->type + 1) << 8 | report->id, ++ ifnum, data, size, 2000); ++ if (rc != -EPIPE) ++ break; ++ ++ usleep_range(1000 << tries, 3000 << tries); ++ } while (++tries < 5); ++ ++ return (rc > 0) ? 0 : rc; ++} ++ ++static bool appletb_disable_autopm(struct hid_device *hdev) ++{ ++ int rc; ++ ++ rc = hid_hw_power(hdev, PM_HINT_FULLON); ++ ++ if (rc == 0) ++ return true; ++ ++ hid_err(hdev, ++ "Failed to disable auto-pm on touch bar device (%d)\n", rc); ++ return false; ++} ++ ++/* ++ * While the mode functionality is listed as a valid hid report in the usb ++ * interface descriptor, on a T1 it's not sent that way. Instead it's sent with ++ * different request-type and without a leading report-id in the data. Hence ++ * we need to send it as a custom usb control message rather via any of the ++ * standard hid_hw_*request() functions. ++ */ ++static int appletb_set_tb_mode(struct appletb_device *tb_dev, ++ unsigned char mode) ++{ ++ struct hid_report *report; ++ void *buf; ++ bool autopm_off = false; ++ int rc; ++ ++ if (!tb_dev->mode_iface.hdev) ++ return -ENOTCONN; ++ ++ report = tb_dev->mode_field->report; ++ ++ if (tb_dev->is_t1) { ++ buf = kmemdup(&mode, 1, GFP_KERNEL); ++ } else { ++ char data[] = { report->id, mode }; ++ buf = kmemdup(data, sizeof(data), GFP_KERNEL); ++ } ++ if (!buf) ++ return -ENOMEM; ++ ++ autopm_off = appletb_disable_autopm(tb_dev->mode_iface.hdev); ++ ++ if (tb_dev->is_t1) ++ rc = appletb_send_usb_ctrl(&tb_dev->mode_iface, ++ USB_DIR_OUT | USB_TYPE_VENDOR | ++ USB_RECIP_DEVICE, ++ report, buf, 1); ++ else ++ rc = appletb_send_usb_ctrl(&tb_dev->mode_iface, ++ USB_DIR_OUT | USB_TYPE_CLASS | ++ USB_RECIP_INTERFACE, ++ report, buf, 2); ++ if (rc < 0) ++ dev_err(tb_dev->log_dev, ++ "Failed to set touch bar mode to %u (%d)\n", mode, rc); ++ ++ if (autopm_off) ++ hid_hw_power(tb_dev->mode_iface.hdev, PM_HINT_NORMAL); ++ ++ kfree(buf); ++ ++ return rc; ++} ++ ++/* ++ * We don't use hid_hw_request() because that doesn't allow us to get the ++ * returned status from the usb-control request; we also don't use ++ * hid_hw_raw_request() because would mean duplicating the retry-on-EPIPE ++ * in our appletb_send_usb_ctrl(). ++ */ ++static int appletb_send_hid_report(struct appletb_iface_info *iface_info, ++ struct hid_report *report) ++{ ++ unsigned char *buf; ++ int rc; ++ ++ buf = hid_alloc_report_buf(report, GFP_KERNEL); ++ if (!buf) ++ return -ENOMEM; ++ ++ hid_output_report(report, buf); ++ ++ rc = appletb_send_usb_ctrl(iface_info, ++ USB_DIR_OUT | USB_TYPE_CLASS | ++ USB_RECIP_INTERFACE, ++ report, buf, hid_report_len(report)); ++ ++ kfree(buf); ++ ++ return rc; ++} ++ ++static int appletb_set_tb_disp(struct appletb_device *tb_dev, ++ unsigned char disp) ++{ ++ struct hid_report *report; ++ int rc; ++ ++ if (!tb_dev->disp_iface.hdev) ++ return -ENOTCONN; ++ ++ report = tb_dev->disp_field->report; ++ ++ rc = hid_set_field(tb_dev->disp_field_aux1, 0, 1); ++ if (rc) { ++ dev_err(tb_dev->log_dev, ++ "Failed to set display report field (%d)\n", rc); ++ return rc; ++ } ++ ++ rc = hid_set_field(tb_dev->disp_field, 0, disp); ++ if (rc) { ++ dev_err(tb_dev->log_dev, ++ "Failed to set display report field (%d)\n", rc); ++ return rc; ++ } ++ ++ /* ++ * Keep the USB interface powered on while the touch bar display is on ++ * for better responsiveness. ++ */ ++ if (disp != APPLETB_CMD_DISP_OFF && !tb_dev->tb_autopm_off) ++ tb_dev->tb_autopm_off = ++ appletb_disable_autopm(report->device); ++ ++ rc = appletb_send_hid_report(&tb_dev->disp_iface, report); ++ if (rc < 0) ++ dev_err(tb_dev->log_dev, ++ "Failed to set touch bar display to %u (%d)\n", disp, ++ rc); ++ ++ if (disp == APPLETB_CMD_DISP_OFF && tb_dev->tb_autopm_off) { ++ hid_hw_power(tb_dev->disp_iface.hdev, PM_HINT_NORMAL); ++ tb_dev->tb_autopm_off = false; ++ } ++ ++ return rc; ++} ++ ++static bool appletb_any_tb_key_pressed(struct appletb_device *tb_dev) ++{ ++ return !!memchr_inv(tb_dev->last_tb_keys_pressed, 0, ++ sizeof(tb_dev->last_tb_keys_pressed)); ++} ++ ++static void appletb_schedule_tb_update(struct appletb_device *tb_dev, s64 secs) ++{ ++ schedule_delayed_work(&tb_dev->tb_work, msecs_to_jiffies(secs * 1000)); ++} ++ ++static void appletb_set_tb_worker(struct work_struct *work) ++{ ++ struct appletb_device *tb_dev = ++ container_of(work, struct appletb_device, tb_work.work); ++ s64 time_left = 0, min_timeout, time_to_off; ++ unsigned char pending_mode; ++ unsigned char pending_disp; ++ unsigned char current_disp; ++ bool restore_autopm; ++ bool any_tb_key_pressed, need_reschedule; ++ int rc1 = 1, rc2 = 1; ++ unsigned long flags; ++ ++ spin_lock_irqsave(&tb_dev->tb_lock, flags); ++ ++ /* handle explicit mode-change request */ ++ pending_mode = tb_dev->pnd_tb_mode; ++ pending_disp = tb_dev->pnd_tb_disp; ++ restore_autopm = tb_dev->restore_autopm; ++ ++ spin_unlock_irqrestore(&tb_dev->tb_lock, flags); ++ ++ if (pending_mode != APPLETB_CMD_MODE_NONE) ++ rc1 = appletb_set_tb_mode(tb_dev, pending_mode); ++ if (pending_mode != APPLETB_CMD_MODE_NONE && ++ pending_disp != APPLETB_CMD_DISP_NONE) ++ msleep(25); ++ if (pending_disp != APPLETB_CMD_DISP_NONE) ++ rc2 = appletb_set_tb_disp(tb_dev, pending_disp); ++ ++ if (restore_autopm && tb_dev->tb_autopm_off) ++ appletb_disable_autopm(tb_dev->disp_field->report->device); ++ ++ spin_lock_irqsave(&tb_dev->tb_lock, flags); ++ ++ need_reschedule = false; ++ ++ if (rc1 == 0) { ++ tb_dev->cur_tb_mode = pending_mode; ++ ++ if (tb_dev->pnd_tb_mode == pending_mode) ++ tb_dev->pnd_tb_mode = APPLETB_CMD_MODE_NONE; ++ else ++ need_reschedule = true; ++ } ++ ++ if (rc2 == 0) { ++ tb_dev->cur_tb_disp = pending_disp; ++ ++ if (tb_dev->pnd_tb_disp == pending_disp) ++ tb_dev->pnd_tb_disp = APPLETB_CMD_DISP_NONE; ++ else ++ need_reschedule = true; ++ } ++ current_disp = tb_dev->cur_tb_disp; ++ ++ tb_dev->restore_autopm = false; ++ ++ /* calculate time left to next timeout */ ++ if (tb_dev->idle_timeout == -2 || tb_dev->idle_timeout == 0) ++ min_timeout = -1; ++ else if (tb_dev->idle_timeout == -1) ++ min_timeout = tb_dev->dim_timeout; ++ else if (tb_dev->dim_timeout <= 0) ++ min_timeout = tb_dev->idle_timeout; ++ else ++ min_timeout = min(tb_dev->dim_timeout, tb_dev->idle_timeout); ++ ++ if (min_timeout > 0) { ++ s64 idle_time = ++ (ktime_ms_delta(ktime_get(), tb_dev->last_event_time) + ++ 500) / 1000; ++ ++ time_left = max(min_timeout - idle_time, 0LL); ++ if (tb_dev->idle_timeout <= 0) ++ time_to_off = -1; ++ else if (idle_time >= tb_dev->idle_timeout) ++ time_to_off = 0; ++ else ++ time_to_off = tb_dev->idle_timeout - idle_time; ++ } else { ++ /* not used - just to appease the compiler */ ++ time_to_off = 0; ++ } ++ ++ any_tb_key_pressed = appletb_any_tb_key_pressed(tb_dev); ++ ++ spin_unlock_irqrestore(&tb_dev->tb_lock, flags); ++ ++ dev_dbg(tb_dev->log_dev, "timeout calc: idle_timeout=%d dim_timeout=%d min_timeout=%lld time_left=%lld need_reschedule=%d any_tb_key_pressed=%d\n", ++ tb_dev->idle_timeout, tb_dev->dim_timeout, min_timeout, ++ time_left, need_reschedule, any_tb_key_pressed); ++ ++ /* a new command arrived while we were busy - handle it */ ++ if (need_reschedule) { ++ appletb_schedule_tb_update(tb_dev, 0); ++ return; ++ } ++ ++ /* if no idle/dim timeout, we're done */ ++ if (min_timeout <= 0) ++ return; ++ ++ /* manage idle/dim timeout */ ++ if (time_left > 0) { ++ /* we fired too soon or had a mode-change - re-schedule */ ++ appletb_schedule_tb_update(tb_dev, time_left); ++ } else if (any_tb_key_pressed) { ++ /* keys are still pressed - re-schedule */ ++ appletb_schedule_tb_update(tb_dev, min_timeout); ++ } else { ++ /* dim or idle timeout reached */ ++ int next_disp = (time_to_off == 0) ? APPLETB_CMD_DISP_OFF : ++ APPLETB_CMD_DISP_DIM; ++ if (next_disp != current_disp && ++ appletb_set_tb_disp(tb_dev, next_disp) == 0) { ++ spin_lock_irqsave(&tb_dev->tb_lock, flags); ++ tb_dev->cur_tb_disp = next_disp; ++ spin_unlock_irqrestore(&tb_dev->tb_lock, flags); ++ } ++ ++ if (time_to_off > 0) ++ appletb_schedule_tb_update(tb_dev, time_to_off); ++ } ++} ++ ++static u16 appletb_fn_to_special(u16 code) ++{ ++ int idx; ++ ++ for (idx = 0; idx < ARRAY_SIZE(appletb_fn_codes); idx++) { ++ if (appletb_fn_codes[idx].from == code) ++ return appletb_fn_codes[idx].to; ++ } ++ ++ return 0; ++} ++ ++static unsigned char appletb_get_cur_tb_mode(struct appletb_device *tb_dev) ++{ ++ return tb_dev->pnd_tb_mode != APPLETB_CMD_MODE_NONE ? ++ tb_dev->pnd_tb_mode : tb_dev->cur_tb_mode; ++} ++ ++static unsigned char appletb_get_cur_tb_disp(struct appletb_device *tb_dev) ++{ ++ return tb_dev->pnd_tb_disp != APPLETB_CMD_DISP_NONE ? ++ tb_dev->pnd_tb_disp : tb_dev->cur_tb_disp; ++} ++ ++static unsigned char appletb_get_fn_tb_mode(struct appletb_device *tb_dev) ++{ ++ switch (tb_dev->fn_mode) { ++ case APPLETB_FN_MODE_ESC: ++ return APPLETB_CMD_MODE_ESC; ++ ++ case APPLETB_FN_MODE_FKEYS: ++ return APPLETB_CMD_MODE_FN; ++ ++ case APPLETB_FN_MODE_SPCL: ++ return APPLETB_CMD_MODE_SPCL; ++ ++ case APPLETB_FN_MODE_INV: ++ return (tb_dev->last_fn_pressed) ? APPLETB_CMD_MODE_SPCL : ++ APPLETB_CMD_MODE_FN; ++ ++ case APPLETB_FN_MODE_NORM: ++ default: ++ return (tb_dev->last_fn_pressed) ? APPLETB_CMD_MODE_FN : ++ APPLETB_CMD_MODE_SPCL; ++ } ++} ++ ++/* ++ * Switch touch bar mode and display when mode or display not the desired ones. ++ */ ++static void appletb_update_touchbar_no_lock(struct appletb_device *tb_dev, ++ bool force) ++{ ++ unsigned char want_mode; ++ unsigned char want_disp; ++ bool need_update = false; ++ ++ /* ++ * Calculate the new modes: ++ * idle_timeout: ++ * -2 mode/disp off ++ * -1 mode on, disp on/dim ++ * 0 mode on, disp off ++ * >0 mode on, disp off after idle_timeout seconds ++ * dim_timeout (only valid if idle_timeout > 0 || idle_timeout == -1): ++ * -1 disp never dimmed ++ * 0 disp always dimmed ++ * >0 disp dim after dim_timeout seconds ++ */ ++ if (tb_dev->idle_timeout == -2) { ++ want_mode = APPLETB_CMD_MODE_OFF; ++ want_disp = APPLETB_CMD_DISP_OFF; ++ } else { ++ want_mode = appletb_get_fn_tb_mode(tb_dev); ++ want_disp = tb_dev->idle_timeout == 0 ? APPLETB_CMD_DISP_OFF : ++ tb_dev->dim_timeout == 0 ? APPLETB_CMD_DISP_DIM : ++ APPLETB_CMD_DISP_ON; ++ } ++ ++ /* ++ * See if we need to update the touch bar, taking into account that we ++ * generally don't want to switch modes while a touch bar key is ++ * pressed. ++ */ ++ if (appletb_get_cur_tb_mode(tb_dev) != want_mode && ++ !appletb_any_tb_key_pressed(tb_dev)) { ++ tb_dev->pnd_tb_mode = want_mode; ++ need_update = true; ++ } ++ ++ if (appletb_get_cur_tb_disp(tb_dev) != want_disp && ++ (!appletb_any_tb_key_pressed(tb_dev) || ++ want_disp != APPLETB_CMD_DISP_OFF)) { ++ tb_dev->pnd_tb_disp = want_disp; ++ need_update = true; ++ } ++ ++ if (force) ++ need_update = true; ++ ++ /* schedule the update if desired */ ++ dev_dbg_ratelimited(tb_dev->log_dev, ++ "update: need_update=%d, want_mode=%d, cur-mode=%d, want_disp=%d, cur-disp=%d\n", ++ need_update, want_mode, tb_dev->cur_tb_mode, ++ want_disp, tb_dev->cur_tb_disp); ++ ++ if (need_update) { ++ cancel_delayed_work(&tb_dev->tb_work); ++ appletb_schedule_tb_update(tb_dev, 0); ++ } ++} ++ ++static void appletb_update_touchbar(struct appletb_device *tb_dev, bool force) ++{ ++ unsigned long flags; ++ ++ spin_lock_irqsave(&tb_dev->tb_lock, flags); ++ ++ if (tb_dev->active) ++ appletb_update_touchbar_no_lock(tb_dev, force); ++ ++ spin_unlock_irqrestore(&tb_dev->tb_lock, flags); ++} ++ ++static void appletb_set_idle_timeout(struct appletb_device *tb_dev, int new) ++{ ++ tb_dev->idle_timeout = new; ++ ++ if (tb_dev->dim_to_is_calc && tb_dev->idle_timeout > 0) ++ tb_dev->dim_timeout = new - min(APPLETB_MAX_DIM_TIME, new / 3); ++ else if (tb_dev->dim_to_is_calc) ++ tb_dev->dim_timeout = -1; ++} ++ ++static ssize_t idle_timeout_show(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ struct appletb_device *tb_dev = dev_get_drvdata(dev); ++ ++ return snprintf(buf, PAGE_SIZE, "%d\n", tb_dev->idle_timeout); ++} ++ ++static ssize_t idle_timeout_store(struct device *dev, ++ struct device_attribute *attr, ++ const char *buf, size_t size) ++{ ++ struct appletb_device *tb_dev = dev_get_drvdata(dev); ++ long new; ++ int rc; ++ ++ rc = kstrtol(buf, 0, &new); ++ if (rc || new > INT_MAX || new < -2) ++ return -EINVAL; ++ ++ appletb_set_idle_timeout(tb_dev, new); ++ appletb_update_touchbar(tb_dev, true); ++ ++ return size; ++} ++ ++static void appletb_set_dim_timeout(struct appletb_device *tb_dev, int new) ++{ ++ if (new == -2) { ++ tb_dev->dim_to_is_calc = true; ++ appletb_set_idle_timeout(tb_dev, tb_dev->idle_timeout); ++ } else { ++ tb_dev->dim_to_is_calc = false; ++ tb_dev->dim_timeout = new; ++ } ++} ++ ++static ssize_t dim_timeout_show(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ struct appletb_device *tb_dev = dev_get_drvdata(dev); ++ ++ return snprintf(buf, PAGE_SIZE, "%d\n", ++ tb_dev->dim_to_is_calc ? -2 : tb_dev->dim_timeout); ++} ++ ++static ssize_t dim_timeout_store(struct device *dev, ++ struct device_attribute *attr, ++ const char *buf, size_t size) ++{ ++ struct appletb_device *tb_dev = dev_get_drvdata(dev); ++ long new; ++ int rc; ++ ++ rc = kstrtol(buf, 0, &new); ++ if (rc || new > INT_MAX || new < -2) ++ return -EINVAL; ++ ++ appletb_set_dim_timeout(tb_dev, new); ++ appletb_update_touchbar(tb_dev, true); ++ ++ return size; ++} ++ ++static ssize_t fnmode_show(struct device *dev, struct device_attribute *attr, ++ char *buf) ++{ ++ struct appletb_device *tb_dev = dev_get_drvdata(dev); ++ ++ return snprintf(buf, PAGE_SIZE, "%d\n", tb_dev->fn_mode); ++} ++ ++static ssize_t fnmode_store(struct device *dev, struct device_attribute *attr, ++ const char *buf, size_t size) ++{ ++ struct appletb_device *tb_dev = dev_get_drvdata(dev); ++ long new; ++ int rc; ++ ++ rc = kstrtol(buf, 0, &new); ++ if (rc || new > APPLETB_FN_MODE_MAX || new < 0) ++ return -EINVAL; ++ ++ tb_dev->fn_mode = new; ++ appletb_update_touchbar(tb_dev, false); ++ ++ return size; ++} ++ ++static int appletb_tb_key_to_slot(unsigned int code) ++{ ++ switch (code) { ++ case KEY_ESC: ++ return 0; ++ case KEY_F1: ++ case KEY_F2: ++ case KEY_F3: ++ case KEY_F4: ++ case KEY_F5: ++ case KEY_F6: ++ case KEY_F7: ++ case KEY_F8: ++ case KEY_F9: ++ case KEY_F10: ++ return code - KEY_F1 + 1; ++ case KEY_F11: ++ case KEY_F12: ++ return code - KEY_F11 + 11; ++ default: ++ return -1; ++ } ++} ++ ++static int appletb_hid_event(struct hid_device *hdev, struct hid_field *field, ++ struct hid_usage *usage, __s32 value) ++{ ++ struct appletb_device *tb_dev = hid_get_drvdata(hdev); ++ unsigned int new_code = 0; ++ unsigned long flags; ++ bool send_dummy = false; ++ bool send_trnsl = false; ++ int slot; ++ int rc = 0; ++ ++ if ((usage->hid & HID_USAGE_PAGE) != HID_UP_KEYBOARD || ++ usage->type != EV_KEY) ++ return 0; ++ ++ /* ++ * Skip non-touch-bar keys. ++ * ++ * Either the touch bar itself or usbhid generate a slew of key-down ++ * events for all the meta keys. None of which we're at all interested ++ * in. ++ */ ++ slot = appletb_tb_key_to_slot(usage->code); ++ if (slot < 0) ++ return 0; ++ ++ spin_lock_irqsave(&tb_dev->tb_lock, flags); ++ ++ if (!tb_dev->active) { ++ spin_unlock_irqrestore(&tb_dev->tb_lock, flags); ++ return 0; ++ } ++ ++ new_code = appletb_fn_to_special(usage->code); ++ ++ if (value != 2) ++ tb_dev->last_tb_keys_pressed[slot] = value; ++ ++ tb_dev->last_event_time = ktime_get(); ++ ++ appletb_update_touchbar_no_lock(tb_dev, false); ++ ++ /* ++ * We want to suppress touch bar keys while the touch bar is off, but ++ * we do want to wake up the screen if it's asleep, so generate a dummy ++ * event in that case. ++ */ ++ if (tb_dev->cur_tb_mode == APPLETB_CMD_MODE_OFF || ++ tb_dev->cur_tb_disp == APPLETB_CMD_DISP_OFF) { ++ send_dummy = true; ++ rc = 1; ++ /* translate special keys */ ++ } else if (new_code && ++ ((value > 0 && ++ appletb_get_cur_tb_mode(tb_dev) == APPLETB_CMD_MODE_SPCL) ++ || ++ (value == 0 && tb_dev->last_tb_keys_translated[slot]))) { ++ tb_dev->last_tb_keys_translated[slot] = true; ++ send_trnsl = true; ++ rc = 1; ++ /* everything else handled normally */ ++ } else { ++ tb_dev->last_tb_keys_translated[slot] = false; ++ } ++ ++ spin_unlock_irqrestore(&tb_dev->tb_lock, flags); ++ ++ /* ++ * Need to send these input events outside of the lock, as otherwise ++ * we can run into the following deadlock: ++ * Task 1 Task 2 ++ * appletb_hid_event() input_event() ++ * acquire tb_lock acquire dev->event_lock ++ * input_event() appletb_inp_event() ++ * acquire dev->event_lock acquire tb_lock ++ */ ++ if (send_dummy) { ++ input_event(field->hidinput->input, EV_KEY, KEY_UNKNOWN, 1); ++ input_event(field->hidinput->input, EV_KEY, KEY_UNKNOWN, 0); ++ } else if (send_trnsl) { ++ input_event(field->hidinput->input, usage->type, new_code, ++ value); ++ } ++ ++ return rc; ++} ++ ++static void appletb_inp_event(struct input_handle *handle, unsigned int type, ++ unsigned int code, int value) ++{ ++ struct appletb_device *tb_dev = handle->private; ++ unsigned long flags; ++ ++ spin_lock_irqsave(&tb_dev->tb_lock, flags); ++ ++ if (!tb_dev->active) { ++ spin_unlock_irqrestore(&tb_dev->tb_lock, flags); ++ return; ++ } ++ ++ if (type == EV_KEY && code == KEY_FN && value != 2) ++ tb_dev->last_fn_pressed = value; ++ ++ tb_dev->last_event_time = ktime_get(); ++ ++ appletb_update_touchbar_no_lock(tb_dev, false); ++ ++ spin_unlock_irqrestore(&tb_dev->tb_lock, flags); ++} ++ ++/* Find and save the usb-device associated with the touch bar input device */ ++static struct usb_interface *appletb_get_usb_iface(struct hid_device *hdev) ++{ ++ struct device *dev = &hdev->dev; ++ ++ while (dev && !(dev->type && dev->type->name && ++ !strcmp(dev->type->name, "usb_interface"))) ++ dev = dev->parent; ++ ++ return dev ? to_usb_interface(dev) : NULL; ++} ++ ++static int appletb_inp_connect(struct input_handler *handler, ++ struct input_dev *dev, ++ const struct input_device_id *id) ++{ ++ struct appletb_device *tb_dev = handler->private; ++ struct input_handle *handle; ++ int rc; ++ ++ if (id->driver_info == APPLETB_DEVID_KEYBOARD) { ++ handle = &tb_dev->kbd_handle; ++ handle->name = "tbkbd"; ++ } else if (id->driver_info == APPLETB_DEVID_TOUCHPAD) { ++ handle = &tb_dev->tpd_handle; ++ handle->name = "tbtpad"; ++ } else { ++ dev_err(tb_dev->log_dev, "Unknown device id (%lu)\n", ++ id->driver_info); ++ return -ENOENT; ++ } ++ ++ if (handle->dev) { ++ dev_err(tb_dev->log_dev, ++ "Duplicate connect to %s input device\n", handle->name); ++ return -EEXIST; ++ } ++ ++ handle->open = 0; ++ handle->dev = input_get_device(dev); ++ handle->handler = handler; ++ handle->private = tb_dev; ++ ++ rc = input_register_handle(handle); ++ if (rc) ++ goto err_free_dev; ++ ++ rc = input_open_device(handle); ++ if (rc) ++ goto err_unregister_handle; ++ ++ dev_dbg(tb_dev->log_dev, "Connected to %s input device\n", ++ handle == &tb_dev->kbd_handle ? "keyboard" : "touchpad"); ++ ++ return 0; ++ ++ err_unregister_handle: ++ input_unregister_handle(handle); ++ err_free_dev: ++ input_put_device(handle->dev); ++ handle->dev = NULL; ++ return rc; ++} ++ ++static void appletb_inp_disconnect(struct input_handle *handle) ++{ ++ struct appletb_device *tb_dev = handle->private; ++ ++ input_close_device(handle); ++ input_unregister_handle(handle); ++ ++ dev_dbg(tb_dev->log_dev, "Disconnected from %s input device\n", ++ handle == &tb_dev->kbd_handle ? "keyboard" : "touchpad"); ++ ++ input_put_device(handle->dev); ++ handle->dev = NULL; ++} ++ ++static int appletb_input_configured(struct hid_device *hdev, ++ struct hid_input *hidinput) ++{ ++ int idx; ++ struct input_dev *input = hidinput->input; ++ ++ /* ++ * Clear various input capabilities that are blindly set by the hid ++ * driver (usbkbd.c) ++ */ ++ memset(input->evbit, 0, sizeof(input->evbit)); ++ memset(input->keybit, 0, sizeof(input->keybit)); ++ memset(input->ledbit, 0, sizeof(input->ledbit)); ++ ++ /* set our actual capabilities */ ++ __set_bit(EV_KEY, input->evbit); ++ __set_bit(EV_REP, input->evbit); ++ __set_bit(EV_MSC, input->evbit); /* hid-input generates MSC_SCAN */ ++ ++ for (idx = 0; idx < ARRAY_SIZE(appletb_fn_codes); idx++) { ++ input_set_capability(input, EV_KEY, appletb_fn_codes[idx].from); ++ input_set_capability(input, EV_KEY, appletb_fn_codes[idx].to); ++ } ++ ++ input_set_capability(input, EV_KEY, KEY_ESC); ++ input_set_capability(input, EV_KEY, KEY_UNKNOWN); ++ ++ return 0; ++} ++ ++static struct appletb_iface_info * ++appletb_get_iface_info(struct appletb_device *tb_dev, struct hid_device *hdev) ++{ ++ if (hdev == tb_dev->mode_iface.hdev) ++ return &tb_dev->mode_iface; ++ if (hdev == tb_dev->disp_iface.hdev) ++ return &tb_dev->disp_iface; ++ return NULL; ++} ++ ++/** ++ * appletb_find_report_field() - Find the field in the report with the given ++ * usage. ++ * @report: the report to search ++ * @field_usage: the usage of the field to search for ++ * ++ * Returns: the hid field if found, or NULL if none found. ++ */ ++static struct hid_field *appletb_find_report_field(struct hid_report *report, ++ unsigned int field_usage) ++{ ++ int f, u; ++ ++ for (f = 0; f < report->maxfield; f++) { ++ struct hid_field *field = report->field[f]; ++ ++ if (field->logical == field_usage) ++ return field; ++ ++ for (u = 0; u < field->maxusage; u++) { ++ if (field->usage[u].hid == field_usage) ++ return field; ++ } ++ } ++ ++ return NULL; ++} ++ ++/** ++ * appletb_find_hid_field() - Search all the reports of the device for the ++ * field with the given usage. ++ * @hdev: the device whose reports to search ++ * @application: the usage of application collection that the field must ++ * belong to ++ * @field_usage: the usage of the field to search for ++ * ++ * Returns: the hid field if found, or NULL if none found. ++ */ ++static struct hid_field *appletb_find_hid_field(struct hid_device *hdev, ++ unsigned int application, ++ unsigned int field_usage) ++{ ++ static const int report_types[] = { HID_INPUT_REPORT, HID_OUTPUT_REPORT, ++ HID_FEATURE_REPORT }; ++ struct hid_report *report; ++ struct hid_field *field; ++ int t; ++ ++ for (t = 0; t < ARRAY_SIZE(report_types); t++) { ++ struct list_head *report_list = ++ &hdev->report_enum[report_types[t]].report_list; ++ list_for_each_entry(report, report_list, list) { ++ if (report->application != application) ++ continue; ++ ++ field = appletb_find_report_field(report, field_usage); ++ if (field) ++ return field; ++ } ++ } ++ ++ return NULL; ++} ++ ++static int appletb_extract_report_and_iface_info(struct appletb_device *tb_dev, ++ struct hid_device *hdev, ++ const struct hid_device_id *id) ++{ ++ struct appletb_iface_info *iface_info; ++ struct usb_interface *usb_iface; ++ struct hid_field *field; ++ ++ field = appletb_find_hid_field(hdev, HID_GD_KEYBOARD, HID_USAGE_MODE); ++ if (field) { ++ iface_info = &tb_dev->mode_iface; ++ tb_dev->mode_field = field; ++ tb_dev->is_t1 = !!(id->driver_data & APPLETB_FEATURE_IS_T1); ++ } else { ++ field = appletb_find_hid_field(hdev, HID_USAGE_APPLE_APP, ++ HID_USAGE_DISP); ++ if (!field) ++ return 0; ++ ++ iface_info = &tb_dev->disp_iface; ++ tb_dev->disp_field = field; ++ tb_dev->disp_field_aux1 = ++ appletb_find_hid_field(hdev, HID_USAGE_APPLE_APP, ++ HID_USAGE_DISP_AUX1); ++ ++ if (!tb_dev->disp_field_aux1 || ++ tb_dev->disp_field_aux1->report != ++ tb_dev->disp_field->report) { ++ dev_err(tb_dev->log_dev, ++ "Unexpected report structure for report %u in device %s\n", ++ tb_dev->disp_field->report->id, ++ dev_name(&hdev->dev)); ++ return -ENODEV; ++ } ++ } ++ ++ usb_iface = appletb_get_usb_iface(hdev); ++ if (!usb_iface) { ++ dev_err(tb_dev->log_dev, ++ "Failed to find usb interface for hid device %s\n", ++ dev_name(&hdev->dev)); ++ return -ENODEV; ++ } ++ ++ iface_info->hdev = hdev; ++ iface_info->usb_iface = usb_get_intf(usb_iface); ++ iface_info->suspended = false; ++ ++ return 1; ++} ++ ++static void appletb_clear_iface_info(struct appletb_device *tb_dev, ++ struct hid_device *hdev) ++{ ++ struct appletb_iface_info *iface_info; ++ ++ iface_info = appletb_get_iface_info(tb_dev, hdev); ++ if (iface_info) { ++ usb_put_intf(iface_info->usb_iface); ++ iface_info->usb_iface = NULL; ++ iface_info->hdev = NULL; ++ } ++} ++ ++static bool appletb_test_and_mark_active(struct appletb_device *tb_dev) ++{ ++ unsigned long flags; ++ bool activated = false; ++ ++ spin_lock_irqsave(&tb_dev->tb_lock, flags); ++ ++ if (tb_dev->mode_iface.hdev && tb_dev->disp_iface.hdev && ++ !tb_dev->active) { ++ tb_dev->active = true; ++ activated = true; ++ } ++ ++ spin_unlock_irqrestore(&tb_dev->tb_lock, flags); ++ ++ return activated; ++} ++ ++static bool appletb_test_and_mark_inactive(struct appletb_device *tb_dev, ++ struct hid_device *hdev) ++{ ++ unsigned long flags; ++ bool deactivated = false; ++ ++ spin_lock_irqsave(&tb_dev->tb_lock, flags); ++ ++ if (tb_dev->mode_iface.hdev && tb_dev->disp_iface.hdev && ++ tb_dev->active && ++ (hdev == tb_dev->mode_iface.hdev || ++ hdev == tb_dev->disp_iface.hdev)) { ++ tb_dev->active = false; ++ deactivated = true; ++ } ++ ++ spin_unlock_irqrestore(&tb_dev->tb_lock, flags); ++ ++ return deactivated; ++} ++ ++static const struct input_device_id appletb_input_devices[] = { ++ { ++ .flags = INPUT_DEVICE_ID_MATCH_BUS | ++ INPUT_DEVICE_ID_MATCH_KEYBIT, ++ .bustype = BUS_SPI, ++ .keybit = { [BIT_WORD(KEY_FN)] = BIT_MASK(KEY_FN) }, ++ .driver_info = APPLETB_DEVID_KEYBOARD, ++ }, /* Builtin SPI keyboard device */ ++ { ++ .flags = INPUT_DEVICE_ID_MATCH_BUS | ++ INPUT_DEVICE_ID_MATCH_KEYBIT, ++ .bustype = BUS_SPI, ++ .keybit = { [BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH) }, ++ .driver_info = APPLETB_DEVID_TOUCHPAD, ++ }, /* Builtin SPI touchpad device */ ++ { ++ .flags = INPUT_DEVICE_ID_MATCH_BUS | ++ INPUT_DEVICE_ID_MATCH_VENDOR | ++ INPUT_DEVICE_ID_MATCH_KEYBIT, ++ .bustype = BUS_USB, ++ .vendor = 0x05ac /* USB_VENDOR_ID_APPLE */, ++ .keybit = { [BIT_WORD(KEY_FN)] = BIT_MASK(KEY_FN) }, ++ .driver_info = APPLETB_DEVID_KEYBOARD, ++ }, /* Builtin USB keyboard device */ ++ { ++ .flags = INPUT_DEVICE_ID_MATCH_BUS | ++ INPUT_DEVICE_ID_MATCH_VENDOR | ++ INPUT_DEVICE_ID_MATCH_KEYBIT, ++ .bustype = BUS_USB, ++ .vendor = 0x05ac /* USB_VENDOR_ID_APPLE */, ++ .keybit = { [BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH) }, ++ .driver_info = APPLETB_DEVID_TOUCHPAD, ++ }, /* Builtin USB touchpad device */ ++ { }, /* Terminating zero entry */ ++}; ++ ++static bool appletb_match_internal_device(struct input_handler *handler, ++ struct input_dev *inp_dev) ++{ ++ struct device *dev = &inp_dev->dev; ++ ++ if (inp_dev->id.bustype == BUS_SPI) ++ return true; ++ ++ /* in kernel: dev && !is_usb_device(dev) */ ++ while (dev && !(dev->type && dev->type->name && ++ !strcmp(dev->type->name, "usb_device"))) ++ dev = dev->parent; ++ ++ /* ++ * Apple labels all their internal keyboards and trackpads as such, ++ * instead of maintaining an ever expanding list of product-id's we ++ * just look at the device's product name. ++ */ ++ if (dev) ++ return !!strstr(to_usb_device(dev)->product, "Internal Keyboard"); ++ ++ return false; ++} ++ ++static int appletb_probe(struct hid_device *hdev, ++ const struct hid_device_id *id) ++{ ++ struct appletb_device *tb_dev = appletb_dev; ++ unsigned long flags; ++ int rc; ++ ++ spin_lock_irqsave(&tb_dev->tb_lock, flags); ++ ++ if (!tb_dev->log_dev) ++ tb_dev->log_dev = &hdev->dev; ++ ++ spin_unlock_irqrestore(&tb_dev->tb_lock, flags); ++ ++ hid_set_drvdata(hdev, tb_dev); ++ ++ /* initialize the report info */ ++ rc = hid_parse(hdev); ++ if (rc) { ++ dev_err(tb_dev->log_dev, "als: hid parse failed (%d)\n", rc); ++ goto error; ++ } ++ ++ rc = appletb_extract_report_and_iface_info(tb_dev, hdev, id); ++ if (rc < 0) ++ goto error; ++ ++ rc = hid_hw_start(hdev, HID_CONNECT_DRIVER | HID_CONNECT_HIDINPUT); ++ if (rc) { ++ dev_err(tb_dev->log_dev, "hw start failed (%d)\n", rc); ++ goto clear_iface_info; ++ } ++ ++ rc = hid_hw_open(hdev); ++ if (rc) { ++ dev_err(tb_dev->log_dev, "hw open failed (%d)\n", rc); ++ goto stop_hid; ++ } ++ ++ /* do setup if we have both interfaces */ ++ if (appletb_test_and_mark_active(tb_dev)) { ++ /* initialize the touch bar */ ++ if (appletb_tb_def_fn_mode >= 0 && ++ appletb_tb_def_fn_mode <= APPLETB_FN_MODE_MAX) ++ tb_dev->fn_mode = appletb_tb_def_fn_mode; ++ else ++ tb_dev->fn_mode = APPLETB_FN_MODE_NORM; ++ appletb_set_idle_timeout(tb_dev, appletb_tb_def_idle_timeout); ++ appletb_set_dim_timeout(tb_dev, appletb_tb_def_dim_timeout); ++ tb_dev->last_event_time = ktime_get(); ++ ++ tb_dev->pnd_tb_mode = APPLETB_CMD_MODE_UPD; ++ tb_dev->pnd_tb_disp = APPLETB_CMD_DISP_UPD; ++ ++ appletb_update_touchbar(tb_dev, false); ++ ++ /* set up the input handler */ ++ tb_dev->inp_handler.event = appletb_inp_event; ++ tb_dev->inp_handler.connect = appletb_inp_connect; ++ tb_dev->inp_handler.disconnect = appletb_inp_disconnect; ++ tb_dev->inp_handler.name = "appletb"; ++ tb_dev->inp_handler.id_table = appletb_input_devices; ++ tb_dev->inp_handler.match = appletb_match_internal_device; ++ tb_dev->inp_handler.private = tb_dev; ++ ++ rc = input_register_handler(&tb_dev->inp_handler); ++ if (rc) { ++ dev_err(tb_dev->log_dev, ++ "Unable to register keyboard handler (%d)\n", ++ rc); ++ goto mark_inactive; ++ } ++ ++ /* initialize sysfs attributes */ ++ rc = sysfs_create_group(&tb_dev->mode_iface.hdev->dev.kobj, ++ &appletb_attr_group); ++ if (rc) { ++ dev_err(tb_dev->log_dev, ++ "Failed to create sysfs attributes (%d)\n", rc); ++ goto unreg_handler; ++ } ++ ++ dev_dbg(tb_dev->log_dev, "Touchbar activated\n"); ++ } ++ ++ return 0; ++ ++unreg_handler: ++ input_unregister_handler(&tb_dev->inp_handler); ++mark_inactive: ++ appletb_test_and_mark_inactive(tb_dev, hdev); ++ cancel_delayed_work_sync(&tb_dev->tb_work); ++ hid_hw_close(hdev); ++stop_hid: ++ hid_hw_stop(hdev); ++clear_iface_info: ++ appletb_clear_iface_info(tb_dev, hdev); ++error: ++ return rc; ++} ++ ++static void appletb_remove(struct hid_device *hdev) ++{ ++ struct appletb_device *tb_dev = hid_get_drvdata(hdev); ++ unsigned long flags; ++ ++ if (appletb_test_and_mark_inactive(tb_dev, hdev)) { ++ sysfs_remove_group(&tb_dev->mode_iface.hdev->dev.kobj, ++ &appletb_attr_group); ++ ++ input_unregister_handler(&tb_dev->inp_handler); ++ ++ cancel_delayed_work_sync(&tb_dev->tb_work); ++ appletb_set_tb_mode(tb_dev, APPLETB_CMD_MODE_OFF); ++ appletb_set_tb_disp(tb_dev, APPLETB_CMD_DISP_ON); ++ ++ if (tb_dev->tb_autopm_off) ++ hid_hw_power(tb_dev->disp_iface.hdev, PM_HINT_NORMAL); ++ ++ dev_info(tb_dev->log_dev, "Touchbar deactivated\n"); ++ } ++ ++ hid_hw_close(hdev); ++ hid_hw_stop(hdev); ++ appletb_clear_iface_info(tb_dev, hdev); ++ ++ spin_lock_irqsave(&tb_dev->tb_lock, flags); ++ ++ if (tb_dev->log_dev == &hdev->dev) { ++ if (tb_dev->mode_iface.hdev) ++ tb_dev->log_dev = &tb_dev->mode_iface.hdev->dev; ++ else if (tb_dev->disp_iface.hdev) ++ tb_dev->log_dev = &tb_dev->disp_iface.hdev->dev; ++ else ++ tb_dev->log_dev = NULL; ++ } ++ ++ spin_unlock_irqrestore(&tb_dev->tb_lock, flags); ++} ++ ++#ifdef CONFIG_PM ++static int appletb_suspend(struct hid_device *hdev, pm_message_t message) ++{ ++ struct appletb_device *tb_dev = hid_get_drvdata(hdev); ++ struct appletb_iface_info *iface_info; ++ unsigned long flags; ++ bool all_suspended = false; ++ ++ if (message.event != PM_EVENT_SUSPEND && ++ message.event != PM_EVENT_FREEZE) ++ return 0; ++ ++ /* ++ * Wait for both interfaces to be suspended and no more async work ++ * in progress. ++ */ ++ spin_lock_irqsave(&tb_dev->tb_lock, flags); ++ ++ if (!tb_dev->mode_iface.suspended && !tb_dev->disp_iface.suspended) { ++ tb_dev->active = false; ++ cancel_delayed_work(&tb_dev->tb_work); ++ } ++ ++ iface_info = appletb_get_iface_info(tb_dev, hdev); ++ if (iface_info) ++ iface_info->suspended = true; ++ ++ if ((!tb_dev->mode_iface.hdev || tb_dev->mode_iface.suspended) && ++ (!tb_dev->disp_iface.hdev || tb_dev->disp_iface.suspended)) ++ all_suspended = true; ++ ++ spin_unlock_irqrestore(&tb_dev->tb_lock, flags); ++ ++ flush_delayed_work(&tb_dev->tb_work); ++ ++ if (!all_suspended) ++ return 0; ++ ++ /* ++ * The touch bar device itself remembers the last state when suspended ++ * in some cases, but in others (e.g. when mode != off and disp == off) ++ * it resumes with a different state; furthermore it may be only ++ * partially responsive in that state. By turning both mode and disp ++ * off we ensure it is in a good state when resuming (and this happens ++ * to be the same state after booting/resuming-from-hibernate, so less ++ * special casing between the two). ++ */ ++ if (message.event == PM_EVENT_SUSPEND) { ++ appletb_set_tb_mode(tb_dev, APPLETB_CMD_MODE_OFF); ++ appletb_set_tb_disp(tb_dev, APPLETB_CMD_DISP_OFF); ++ } ++ ++ spin_lock_irqsave(&tb_dev->tb_lock, flags); ++ ++ tb_dev->cur_tb_mode = APPLETB_CMD_MODE_OFF; ++ tb_dev->cur_tb_disp = APPLETB_CMD_DISP_OFF; ++ ++ spin_unlock_irqrestore(&tb_dev->tb_lock, flags); ++ ++ dev_info(tb_dev->log_dev, "Touchbar suspended.\n"); ++ ++ return 0; ++} ++ ++static int appletb_reset_resume(struct hid_device *hdev) ++{ ++ struct appletb_device *tb_dev = hid_get_drvdata(hdev); ++ struct appletb_iface_info *iface_info; ++ unsigned long flags; ++ ++ spin_lock_irqsave(&tb_dev->tb_lock, flags); ++ ++ iface_info = appletb_get_iface_info(tb_dev, hdev); ++ if (iface_info) ++ iface_info->suspended = false; ++ ++ if ((tb_dev->mode_iface.hdev && !tb_dev->mode_iface.suspended) && ++ (tb_dev->disp_iface.hdev && !tb_dev->disp_iface.suspended)) { ++ /* ++ * Restore touch bar state. Note that autopm state is not ++ * preserved, so need explicitly restore that here. ++ */ ++ tb_dev->active = true; ++ tb_dev->restore_autopm = true; ++ tb_dev->last_event_time = ktime_get(); ++ ++ appletb_update_touchbar_no_lock(tb_dev, true); ++ ++ dev_info(tb_dev->log_dev, "Touchbar resumed.\n"); ++ } ++ ++ spin_unlock_irqrestore(&tb_dev->tb_lock, flags); ++ ++ return 0; ++} ++#endif ++ ++static struct appletb_device *appletb_alloc_device(void) ++{ ++ struct appletb_device *tb_dev; ++ ++ tb_dev = kzalloc(sizeof(*tb_dev), GFP_KERNEL); ++ if (!tb_dev) ++ return NULL; ++ ++ spin_lock_init(&tb_dev->tb_lock); ++ INIT_DELAYED_WORK(&tb_dev->tb_work, appletb_set_tb_worker); ++ ++ return tb_dev; ++} ++ ++static void appletb_free_device(struct appletb_device *tb_dev) ++{ ++ cancel_delayed_work_sync(&tb_dev->tb_work); ++ kfree(tb_dev); ++} ++ ++static const struct hid_device_id appletb_hid_ids[] = { ++ /* MacBook Pro's 2016, 2017, with T1 chip */ ++ { HID_USB_DEVICE(USB_VENDOR_ID_LINUX_FOUNDATION, ++ USB_DEVICE_ID_IBRIDGE_TB), ++ .driver_data = APPLETB_FEATURE_IS_T1 }, ++ /* MacBook Pro's 2018, 2019, with T2 chip: iBridge DFR brightness */ ++ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, 0x8102) }, ++ /* MacBook Pro's 2018, 2019, with T2 chip: iBridge Display */ ++ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, 0x8302) }, ++ { }, ++}; ++ ++MODULE_DEVICE_TABLE(hid, appletb_hid_ids); ++ ++static struct hid_driver appletb_hid_driver = { ++ .name = "apple-touchbar", ++ .id_table = appletb_hid_ids, ++ .probe = appletb_probe, ++ .remove = appletb_remove, ++ .event = appletb_hid_event, ++ .input_configured = appletb_input_configured, ++#ifdef CONFIG_PM ++ .suspend = appletb_suspend, ++ .reset_resume = appletb_reset_resume, ++#endif ++}; ++ ++static int __init appletb_init(void) ++{ ++ struct appletb_device *tb_dev; ++ int rc; ++ ++ tb_dev = appletb_alloc_device(); ++ if (!tb_dev) ++ return -ENOMEM; ++ ++ appletb_dev = tb_dev; ++ ++ rc = hid_register_driver(&appletb_hid_driver); ++ if (rc) ++ goto error; ++ ++ return 0; ++ ++error: ++ appletb_free_device(tb_dev); ++ return rc; ++} ++ ++static void __exit appletb_exit(void) ++{ ++ hid_unregister_driver(&appletb_hid_driver); ++ appletb_free_device(appletb_dev); ++} ++ ++module_init(appletb_init); ++module_exit(appletb_exit); ++ ++MODULE_AUTHOR("Ronald Tschalär"); ++MODULE_DESCRIPTION("MacBookPro Touch Bar driver"); ++MODULE_LICENSE("GPL v2"); +-- +2.38.1 + diff --git a/1008-HID-apple-touchbar-support-magic-keyboard-backlight.patch b/1008-HID-apple-touchbar-support-magic-keyboard-backlight.patch new file mode 100644 index 0000000..1e9cc3f --- /dev/null +++ b/1008-HID-apple-touchbar-support-magic-keyboard-backlight.patch @@ -0,0 +1,233 @@ +From c36014bd8cbf435a3668dbe257cb3d4293968074 Mon Sep 17 00:00:00 2001 +From: Orlando Chamberlain +Date: Sun, 6 Nov 2022 19:23:56 +0300 +Subject: [PATCH 5/6] HID: apple-touchbar: support magic keyboard backlight + +MacBookPro16,1/2/3/4 have the magic keyboard, with the keyboard +backlight controlled via the Touch Bar Backlight USB device. + +We check that the internal keyboard's USB product ID matches that of +models with the magic keyboard, and then we create a sysfs led class +dev that controls the backlight. + +NOTE: This commit hasn't been signed off yet +--- + drivers/hid/apple-touchbar.c | 167 +++++++++++++++++++++++++++++++++++ + 1 file changed, 167 insertions(+) + +diff --git a/drivers/hid/apple-touchbar.c b/drivers/hid/apple-touchbar.c +index 87cb9ebafb61..e2cf41b35363 100644 +--- a/drivers/hid/apple-touchbar.c ++++ b/drivers/hid/apple-touchbar.c +@@ -78,6 +78,8 @@ + + #define APPLETB_FEATURE_IS_T1 BIT(0) + ++#define APPLE_MAGIC_KBD_BL_MAX 60 ++ + static int appletb_tb_def_idle_timeout = 5 * 60; + module_param_named(idle_timeout, appletb_tb_def_idle_timeout, int, 0444); + MODULE_PARM_DESC(idle_timeout, "Default touch bar idle timeout:\n" +@@ -136,6 +138,12 @@ static const struct attribute_group appletb_attr_group = { + .attrs = appletb_attrs, + }; + ++struct apple_magic_backlight { ++ struct led_classdev cdev; ++ struct usb_device *dev; ++ bool powered; ++}; ++ + struct appletb_device { + bool active; + struct device *log_dev; +@@ -175,6 +183,8 @@ struct appletb_device { + int fn_mode; + + bool is_t1; ++ ++ struct apple_magic_backlight kbd_backlight; + }; + + struct appletb_key_translation { +@@ -197,6 +207,30 @@ static const struct appletb_key_translation appletb_fn_codes[] = { + { KEY_F12, KEY_VOLUMEUP }, + }; + ++struct apple_magic_keyboard_backlight_brightness_report { ++ u8 report_id; /* 0x01 */ ++ u8 mode; /* If 0x00, brightness can turn off backlight */ ++ u8 brightness; ++ u8 override_1; /* If these are non-zero, backlight is overridden to max brightness */ ++ u8 override_2; ++ u8 max; /* Lower is brighter, only takes effect when turning backlight ++ * on from off, can be unreliable ++ */ ++ u8 rate; ++ u8 magic_1; /* If these are non-zero, we are ignored. */ ++ u8 magic_2; ++}; ++ ++struct apple_magic_keyboard_backlight_power_report { ++ u8 report_id; /* 0x03 */ ++ u8 power; ++ u8 max; /* Lower is brighter, only takes effect when turning backlight ++ * on from off, can be unreliable ++ */ ++ u8 rate; ++ u8 magic_1; /* If these are non-zero, we are ignored. */ ++ u8 magic_2; ++}; + static struct appletb_device *appletb_dev; + + static int appletb_send_usb_ctrl(struct appletb_iface_info *iface_info, +@@ -317,6 +351,132 @@ static int appletb_send_hid_report(struct appletb_iface_info *iface_info, + return rc; + } + ++static int apple_magic_keyboard_backlight_power_set(struct apple_magic_backlight *backlight, ++ char power, char rate) ++{ ++ int tries = 0; ++ int rc; ++ struct apple_magic_keyboard_backlight_power_report *rep; ++ ++ rep = kmalloc(sizeof(*rep), GFP_KERNEL); ++ if (rep == NULL) ++ return -ENOMEM; ++ ++ backlight->powered = power ? true : false; ++ ++ rep->report_id = 0x03; ++ rep->power = power; ++ rep->max = 0x5e; /* Windows uses 0x5e when turning on, and 0xf4 when ++ * turning off. When it's off it doesn't matter, so ++ * use 0x5e ++ */ ++ rep->rate = rate; ++ ++ do { ++ /* ++ * FIXME: use appletb_send_hid_report, don't hard code all of this ++ * Need to get apple_tb_send_hid_report to use wIndex=0x01 ++ */ ++ rc = usb_control_msg(backlight->dev, ++ usb_sndctrlpipe(backlight->dev, 0), ++ HID_REQ_SET_REPORT, USB_DIR_OUT | ++ USB_TYPE_CLASS | USB_RECIP_INTERFACE, ++ 0x0303, 0x01, rep, sizeof(*rep), ++ 2000); ++ if (rc != -EPIPE) ++ break; ++ ++ usleep_range(1000 << tries, 3000 << tries); ++ } while (++tries < 5); ++ ++ kfree(rep); ++ return (rc > 0) ? 0 : rc; ++} ++ ++static int apple_magic_keyboard_backlight_brightness_set(struct apple_magic_backlight *backlight, ++ char brightness, char rate) ++{ ++ int tries = 0; ++ int rc; ++ struct apple_magic_keyboard_backlight_brightness_report *rep; ++ ++ rep = kmalloc(sizeof(*rep), GFP_KERNEL); ++ if (rep == NULL) ++ return -ENOMEM; ++ ++ rep->report_id = 0x01; ++ rep->mode = brightness; ++ rep->brightness = brightness; ++ rep->max = 0x5e; ++ rep->rate = rate; ++ ++ do { ++ /* ++ * FIXME: use appletb_send_hid_report, don't hard code all of this ++ * Need to get apple_tb_send_hid_report to use wIndex=0x01 ++ */ ++ rc = usb_control_msg(backlight->dev, ++ usb_sndctrlpipe(backlight->dev, 0), ++ HID_REQ_SET_REPORT, USB_DIR_OUT | ++ USB_TYPE_CLASS | USB_RECIP_INTERFACE, ++ 0x0301, 0x01, rep, sizeof(*rep), ++ 2000); ++ if (rc != -EPIPE) ++ break; ++ ++ usleep_range(1000 << tries, 3000 << tries); ++ } while (++tries < 5); ++ ++ kfree(rep); ++ return (rc > 0) ? 0 : rc; ++} ++ ++static int apple_magic_keyboard_backlight_set(struct apple_magic_backlight *backlight, ++ char brightness, char rate) ++{ ++ int rc; ++ ++ if (!brightness) ++ return apple_magic_keyboard_backlight_power_set(backlight, 0, rate); ++ ++ rc = apple_magic_keyboard_backlight_brightness_set(backlight, brightness, rate); ++ if (rc) ++ return rc; ++ ++ if (!backlight->powered && brightness) ++ rc = apple_magic_keyboard_backlight_power_set(backlight, 1, rate); ++ ++ return (rc > 0) ? 0 : rc; ++} ++ ++static int apple_magic_keyboard_backlight_led_set(struct led_classdev *led_cdev, ++ enum led_brightness brightness) ++{ ++ struct apple_magic_backlight *backlight = container_of(led_cdev, ++ struct apple_magic_backlight, cdev); ++ ++ return apple_magic_keyboard_backlight_set(backlight, brightness, 1); ++} ++ ++static int apple_magic_keyboard_backlight_init(struct appletb_device *tb_dev) ++{ ++ int ret; ++ ++ tb_dev->kbd_backlight.dev = interface_to_usbdev(tb_dev->disp_iface.usb_iface); ++ tb_dev->kbd_backlight.cdev.name = "apple::kbd_backlight"; ++ tb_dev->kbd_backlight.cdev.max_brightness = APPLE_MAGIC_KBD_BL_MAX; ++ tb_dev->kbd_backlight.cdev.brightness_set_blocking = apple_magic_keyboard_backlight_led_set; ++ ++ ret = apple_magic_keyboard_backlight_set(&tb_dev->kbd_backlight, 0, 0); ++ if (ret) { ++ dev_err(tb_dev->log_dev, ++ "Failed to initialise Magic Keyboard Backlight (%d)\n", ret); ++ return ret; ++ } ++ ++ return devm_led_classdev_register(tb_dev->log_dev, &tb_dev->kbd_backlight.cdev); ++} ++ + static int appletb_set_tb_disp(struct appletb_device *tb_dev, + unsigned char disp) + { +@@ -881,6 +1041,13 @@ static int appletb_inp_connect(struct input_handler *handler, + if (id->driver_info == APPLETB_DEVID_KEYBOARD) { + handle = &tb_dev->kbd_handle; + handle->name = "tbkbd"; ++ switch (dev->id.product) { ++ case 0x0340u: /* MacBookPro16,1/4 */ ++ case 0x027eu: /* MacBookPro16,2 */ ++ case 0x027fu: /* MacBookPro16,3 */ ++ apple_magic_keyboard_backlight_init(tb_dev); ++ break; ++ } + } else if (id->driver_info == APPLETB_DEVID_TOUCHPAD) { + handle = &tb_dev->tpd_handle; + handle->name = "tbtpad"; +-- +2.38.1 + diff --git a/1009-HID-quirks-Use-touchbar-quirks-when-touchbar-driver-.patch b/1009-HID-quirks-Use-touchbar-quirks-when-touchbar-driver-.patch new file mode 100644 index 0000000..60c8f9b --- /dev/null +++ b/1009-HID-quirks-Use-touchbar-quirks-when-touchbar-driver-.patch @@ -0,0 +1,35 @@ +From 17d3c266246722505ef5bddd470d532d2dd75a3f Mon Sep 17 00:00:00 2001 +From: Aditya Garg +Date: Mon, 7 Nov 2022 01:17:17 +0530 +Subject: [PATCH 6/6] HID: quirks: Use touchbar quirks when touchbar driver is + enabled + +Signed-off-by: Aditya Garg +--- + drivers/hid/hid-quirks.c | 6 ++++-- + 1 file changed, 4 insertions(+), 2 deletions(-) + +diff --git a/drivers/hid/hid-quirks.c b/drivers/hid/hid-quirks.c +index cc862fbf1995..32ff8b7b336f 100644 +--- a/drivers/hid/hid-quirks.c ++++ b/drivers/hid/hid-quirks.c +@@ -314,12 +314,14 @@ static const struct hid_device_id hid_have_special_driver[] = { + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER1_TP_ONLY) }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_MAGIC_KEYBOARD_2021) }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_MAGIC_KEYBOARD_FINGERPRINT_2021) }, +- { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_TOUCHBAR_BACKLIGHT) }, +- { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_TOUCHBAR_DISPLAY) }, + #endif + #if IS_ENABLED(CONFIG_HID_APPLE_IBRIDGE) + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_IBRIDGE) }, + #endif ++#if IS_ENABLED(CONFIG_HID_APPLE_TOUCHBAR) ++ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_TOUCHBAR_BACKLIGHT) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_TOUCHBAR_DISPLAY) }, ++#endif + #if IS_ENABLED(CONFIG_HID_APPLEIR) + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_IRCONTROL) }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_IRCONTROL2) }, +-- +2.38.1 + diff --git a/2002-nvram.patch b/2002-nvram.patch deleted file mode 100644 index 246b7a6..0000000 --- a/2002-nvram.patch +++ /dev/null @@ -1,144 +0,0 @@ -From 4f50f041554c78aaa821585ce583946ecdae91b3 Mon Sep 17 00:00:00 2001 -From: Ard Biesheuvel -Date: Thu, 27 Oct 2022 15:52:31 +0200 -Subject: efi: efivars: Fix variable writes with unsupported - query_variable_store() - -Commit 8a254d90a775 ("efi: efivars: Fix variable writes without -query_variable_store()") addressed an issue that was introduced during -the EFI variable store refactor, where alternative implementations of -the efivars layer that lacked query_variable_store() would no longer -work. - -Unfortunately, there is another case to consider here, which was missed: -if the efivars layer is backed by the EFI runtime services as usual, but -the EFI implementation predates the introduction of QueryVariableInfo(), -we will return EFI_UNSUPPORTED, and this is no longer being dealt with -correctly. - -So let's fix this, and while at it, clean up the code a bit, by merging -the check_var_size() routines as well as their callers. - -Cc: # v6.0 -Fixes: bbc6d2c6ef22 ("efi: vars: Switch to new wrapper layer") -Signed-off-by: Ard Biesheuvel ---- - drivers/firmware/efi/vars.c | 68 +++++++++++++-------------------------------- - 1 file changed, 20 insertions(+), 48 deletions(-) - -diff --git a/drivers/firmware/efi/vars.c b/drivers/firmware/efi/vars.c -index 433b615871395..0ba9f18312f5b 100644 ---- a/drivers/firmware/efi/vars.c -+++ b/drivers/firmware/efi/vars.c -@@ -21,29 +21,22 @@ static struct efivars *__efivars; - - static DEFINE_SEMAPHORE(efivars_lock); - --static efi_status_t check_var_size(u32 attributes, unsigned long size) --{ -- const struct efivar_operations *fops; -- -- fops = __efivars->ops; -- -- if (!fops->query_variable_store) -- return (size <= SZ_64K) ? EFI_SUCCESS : EFI_OUT_OF_RESOURCES; -- -- return fops->query_variable_store(attributes, size, false); --} -- --static --efi_status_t check_var_size_nonblocking(u32 attributes, unsigned long size) -+static efi_status_t check_var_size(bool nonblocking, u32 attributes, -+ unsigned long size) - { - const struct efivar_operations *fops; -+ efi_status_t status; - - fops = __efivars->ops; - - if (!fops->query_variable_store) -+ status = EFI_UNSUPPORTED; -+ else -+ status = fops->query_variable_store(attributes, size, -+ nonblocking); -+ if (status == EFI_UNSUPPORTED) - return (size <= SZ_64K) ? EFI_SUCCESS : EFI_OUT_OF_RESOURCES; -- -- return fops->query_variable_store(attributes, size, true); -+ return status; - } - - /** -@@ -195,26 +188,6 @@ efi_status_t efivar_get_next_variable(unsigned long *name_size, - } - EXPORT_SYMBOL_NS_GPL(efivar_get_next_variable, EFIVAR); - --/* -- * efivar_set_variable_blocking() - local helper function for set_variable -- * -- * Must be called with efivars_lock held. -- */ --static efi_status_t --efivar_set_variable_blocking(efi_char16_t *name, efi_guid_t *vendor, -- u32 attr, unsigned long data_size, void *data) --{ -- efi_status_t status; -- -- if (data_size > 0) { -- status = check_var_size(attr, data_size + -- ucs2_strsize(name, 1024)); -- if (status != EFI_SUCCESS) -- return status; -- } -- return __efivars->ops->set_variable(name, vendor, attr, data_size, data); --} -- - /* - * efivar_set_variable_locked() - set a variable identified by name/vendor - * -@@ -228,23 +201,21 @@ efi_status_t efivar_set_variable_locked(efi_char16_t *name, efi_guid_t *vendor, - efi_set_variable_t *setvar; - efi_status_t status; - -- if (!nonblocking) -- return efivar_set_variable_blocking(name, vendor, attr, -- data_size, data); -+ if (data_size > 0) { -+ status = check_var_size(nonblocking, attr, -+ data_size + ucs2_strsize(name, 1024)); -+ if (status != EFI_SUCCESS) -+ return status; -+ } - - /* - * If no _nonblocking variant exists, the ordinary one - * is assumed to be non-blocking. - */ -- setvar = __efivars->ops->set_variable_nonblocking ?: -- __efivars->ops->set_variable; -+ setvar = __efivars->ops->set_variable_nonblocking; -+ if (!setvar || !nonblocking) -+ setvar = __efivars->ops->set_variable; - -- if (data_size > 0) { -- status = check_var_size_nonblocking(attr, data_size + -- ucs2_strsize(name, 1024)); -- if (status != EFI_SUCCESS) -- return status; -- } - return setvar(name, vendor, attr, data_size, data); - } - EXPORT_SYMBOL_NS_GPL(efivar_set_variable_locked, EFIVAR); -@@ -264,7 +235,8 @@ efi_status_t efivar_set_variable(efi_char16_t *name, efi_guid_t *vendor, - if (efivar_lock()) - return EFI_ABORTED; - -- status = efivar_set_variable_blocking(name, vendor, attr, data_size, data); -+ status = efivar_set_variable_locked(name, vendor, attr, data_size, -+ data, false); - efivar_unlock(); - return status; - } --- -cgit -