mirror of
https://github.com/a52xq-dev/kernel.git
synced 2026-10-18 21:58:11 +07:00
platform-drivers-x86 for v4.15-1
For this cycle we have quite an update for the Dell SMBIOS driver including WMI work to provide an interface for SMBIOS tokens via sysfs and WMI support for 2017+ Dell laptop models. SMM dispatcher code is split into a separate driver followed by a new WMI dispatcher. The latter provides a character device interface to user space. The pull request contains a merge of immutable branch from Wolfram Sang in order to apply a dependent fix to the Intel CherryTrail Battery Management driver. Other Intel drivers got a lot of cleanups. The Turbo Boost Max 3.0 support is added for Intel Skylake. Peaq WMI hotkeys driver gets its own maintainer and white list of supported models. Silead DMI is expanded to support few additional platforms. Tablet mode via GMMS ACPI method is added to support some ThinkPad tablets. Two commits appear here which were previously merged during the v4.14-rcX cycle: -d7ca5ebf24platform/x86: intel_pmc_ipc: Use devm_* calls in driver probe function -e3075fd6f8platform/x86: intel_pmc_ipc: Use spin_lock to protect GCR updates Add driver to force WMI Thunderbolt controller power status: - Add driver to force WMI Thunderbolt controller power status asus-wmi: - Add lightbar led support dell-laptop: - Allocate buffer before rfkill use dell-smbios: - fix string overflow - Add filtering support - Introduce dispatcher for SMM calls - Add a sysfs interface for SMBIOS tokens - only run if proper oem string is detected - Prefix class/select with cmd_ - Add pr_fmt definition to driver dell-smbios-smm: - test for WSMT dell-smbios-wmi: - release mutex lock on WMI call failure - introduce userspace interface - Add new WMI dispatcher driver dell-smo8800: - remove redundant assignments to byte_data dell-wmi: - don't check length returned - clean up wmi descriptor check - increase severity of some failures - Do not match on descriptor GUID modalias - Label driver as handling notifications dell-*wmi*: - Relay failed initial probe to dependent drivers dell-wmi-descriptor: - check if memory was allocated - split WMI descriptor into it's own driver fujitsu-laptop: - Fix radio LED detection - Don't oops when FUJ02E3 is not presnt hp_accel: - Add quirk for HP ProBook 440 G4 hp-wmi: - Fix tablet mode detection for convertibles ideapad-laptop: - Add Lenovo Yoga 920-13IKB to no_hw_rfkill dmi list intel_cht_int33fe: - Update fusb302 type string, add properties - make a couple of local functions static - Work around BIOS bug on some devices intel-hid: - Power button suspend on Dell Latitude 7275 intel_ips: - Convert timers to use timer_setup() - Remove FSF address from GPL notice - Remove unneeded fields and label - Keep pointer to struct device - Use PCI_VDEVICE() macro - Switch to new PCI IRQ allocation API - Simplify error handling via devres API intel_pmc_ipc: - Revert Use MFD framework to create dependent devices - Use MFD framework to create dependent devices - Use spin_lock to protect GCR updates - Use devm_* calls in driver probe function intel_punit_ipc: - Fix resource ioremap warning intel_telemetry: - Remove useless default in Kconfig - Add needed inclusion - cleanup redundant headers - Fix typos - Fix load failure info intel_telemetry_debugfs: - Use standard ARRAY_SIZE() macro intel_turbo_max_3: - Add Skylake platform intel-wmi-thunderbolt: - Silence error cases MAINTAINERS: - Add entry for the PEAQ WMI hotkeys driver mlx-platform: - make a couple of structures static peaq_wmi: - Fix missing terminating entry for peaq_dmi_table peaq-wmi: - Remove unnecessary checks from peaq_wmi_exit - Add DMI check before binding to the WMI interface - Revert Blacklist Lenovo ideapad 700-15ISK - Blacklist Lenovo ideapad 700-15ISK silead_dmi: - Add silead, home-button property to some tablets - Add entry for the Digma e200 tablet - Fix GP-electronic T701 entry - Add entry for the Chuwi Hi8 Pro tablet sony-laptop: - Drop variable assignment in sony_nc_setup_rfkill() - Fix error handling in sony_nc_setup_rfkill() thinkpad_acpi: - Implement tablet mode using GMMS method tools/wmi: - add a sample for dell smbios communication over WMI wmi: - release mutex on module acquistion failure - create userspace interface for drivers - Don't allow drivers to get each other's GUIDs - Add new method wmidev_evaluate_method - Destroy on cleanup rather than unregister - Cleanup exit routine in reverse order of init - Sort include list -----BEGIN PGP SIGNATURE----- iQIzBAABCgAdFiEEhiZOUlnC9oKN3n3AmT3/83c5Sy0FAloQcs0ACgkQmT3/83c5 Sy2agQ//WJirmlqR4WMSDynq0w/rKomx7qMZvabbIxFc7n5beCUrlc61lt4x/cPN 0HrdkNcYvkW21j8q/Lk5RDlK45JLZLgtDb1rIGjx8Pkf+EXyKwNb4UtqaZuwZRN6 nVnSGAdShF6f4OqVQU+ZuRr2nQvBlK4OyopKuRaq0kuejJjMLq1sc7VNidvb9zdj w4zfhh8RQ2KV3NK8g0k/uQu573Y+tCDUQvxQj5YIgC4RWbv4ZTuZD1Hbjc3Vdy9+ z0RvrUBCikM0CTqVyXn/ncgiMnAj6oeLdB76F1dlaIaAH/WtisEWr3hLWHskdJwW N+qGBB0K72gUHwr4Ck4x2s/S76XeiFYD0BeHL3ODn6ZlKoh7gV2NVwYFDNQHd+uc MXN9Xzs3lRkiDFyoXbtBG0wZRn2rS1rhGSYDYQUKg8Qf7MirwEVdYA0CZKeXAA78 2V29FNdvUmWiL/ZiA/2peeuUTWgPdzENDLHXzE/jOCGBS2/pL4znYv2ZSJ+HIeyW oCVMxasSI3pMLxCU8KxgA1QwrbDoiFTYYXCa1Ighq9o2ZEoy7Ub1XmpBB9HKHby7 K6zQcCYcf+SyscIqvu50ZImZ8nt5FnIdPSrTOX/FMM0Niv42mfKwD+UniG+P5K1z OLCIEm74NE9jRjzxuW5E1DSo56gJHUbxxy1rOFzgJnGQ+CAVXKo= =upqs -----END PGP SIGNATURE----- Merge tag 'platform-drivers-x86-v4.15-1' of git://git.infradead.org/linux-platform-drivers-x86 Pull x86 platform driver updates from Andy Shevchenko: "Here is the collected material against Platform Drivers x86 subsystem. It's rather bit busy cycle for PDx86, mostly due to Dell SMBIOS driver activity For this cycle we have quite an update for the Dell SMBIOS driver including WMI work to provide an interface for SMBIOS tokens via sysfs and WMI support for 2017+ Dell laptop models. SMM dispatcher code is split into a separate driver followed by a new WMI dispatcher. The latter provides a character device interface to user space. The git history also contains a merge of immutable branch from Wolfram Sang in order to apply a dependent fix to the Intel CherryTrail Battery Management driver. Other Intel drivers got a lot of cleanups. The Turbo Boost Max 3.0 support is added for Intel Skylake. Peaq WMI hotkeys driver gets its own maintainer and white list of supported models. Silead DMI is expanded to support few additional platforms. Tablet mode via GMMS ACPI method is added to support some ThinkPad tablets. new driver: - Add driver to force WMI Thunderbolt controller power status asus-wmi: - Add lightbar led support dell-laptop: - Allocate buffer before rfkill use dell-smbios: - fix string overflow - Add filtering support - Introduce dispatcher for SMM calls - Add a sysfs interface for SMBIOS tokens - only run if proper oem string is detected - Prefix class/select with cmd_ - Add pr_fmt definition to driver dell-smbios-smm: - test for WSMT dell-smbios-wmi: - release mutex lock on WMI call failure - introduce userspace interface - Add new WMI dispatcher driver dell-smo8800: - remove redundant assignments to byte_data dell-wmi: - don't check length returned - clean up wmi descriptor check - increase severity of some failures - Do not match on descriptor GUID modalias - Label driver as handling notifications dell-*wmi*: - Relay failed initial probe to dependent drivers dell-wmi-descriptor: - check if memory was allocated - split WMI descriptor into it's own driver fujitsu-laptop: - Fix radio LED detection - Don't oops when FUJ02E3 is not presnt hp_accel: - Add quirk for HP ProBook 440 G4 hp-wmi: - Fix tablet mode detection for convertibles ideapad-laptop: - Add Lenovo Yoga 920-13IKB to no_hw_rfkill dmi list intel_cht_int33fe: - Update fusb302 type string, add properties - make a couple of local functions static - Work around BIOS bug on some devices intel-hid: - Power button suspend on Dell Latitude 7275 intel_ips: - Convert timers to use timer_setup() - Remove FSF address from GPL notice - Remove unneeded fields and label - Keep pointer to struct device - Use PCI_VDEVICE() macro - Switch to new PCI IRQ allocation API - Simplify error handling via devres API intel_pmc_ipc: - Revert Use MFD framework to create dependent devices - Use MFD framework to create dependent devices - Use spin_lock to protect GCR updates - Use devm_* calls in driver probe function intel_punit_ipc: - Fix resource ioremap warning intel_telemetry: - Remove useless default in Kconfig - Add needed inclusion - cleanup redundant headers - Fix typos - Fix load failure info intel_telemetry_debugfs: - Use standard ARRAY_SIZE() macro intel_turbo_max_3: - Add Skylake platform intel-wmi-thunderbolt: - Silence error cases mlx-platform: - make a couple of structures static peaq_wmi: - Fix missing terminating entry for peaq_dmi_table peaq-wmi: - Remove unnecessary checks from peaq_wmi_exit - Add DMI check before binding to the WMI interface - Revert Blacklist Lenovo ideapad 700-15ISK - Blacklist Lenovo ideapad 700-15ISK silead_dmi: - Add silead, home-button property to some tablets - Add entry for the Digma e200 tablet - Fix GP-electronic T701 entry - Add entry for the Chuwi Hi8 Pro tablet sony-laptop: - Drop variable assignment in sony_nc_setup_rfkill() - Fix error handling in sony_nc_setup_rfkill() thinkpad_acpi: - Implement tablet mode using GMMS method tools/wmi: - add a sample for dell smbios communication over WMI wmi: - release mutex on module acquistion failure - create userspace interface for drivers - Don't allow drivers to get each other's GUIDs - Add new method wmidev_evaluate_method - Destroy on cleanup rather than unregister - Cleanup exit routine in reverse order of init - Sort include list" * tag 'platform-drivers-x86-v4.15-1' of git://git.infradead.org/linux-platform-drivers-x86: (74 commits) platform/x86: silead_dmi: Add silead, home-button property to some tablets platform/x86: dell-laptop: Allocate buffer before rfkill use platform/x86: dell-*wmi*: Relay failed initial probe to dependent drivers platform/x86: dell-wmi-descriptor: check if memory was allocated platform/x86: Revert intel_pmc_ipc: Use MFD framework to create dependent devices platform/x86: dell-smbios-wmi: release mutex lock on WMI call failure platform/x86: wmi: release mutex on module acquistion failure platform/x86: dell-smbios: fix string overflow platform/x86: intel_pmc_ipc: Use MFD framework to create dependent devices platform/x86: intel_punit_ipc: Fix resource ioremap warning platform/x86: dell-smo8800: remove redundant assignments to byte_data platform/x86: hp-wmi: Fix tablet mode detection for convertibles platform/x86: intel_ips: Convert timers to use timer_setup() platform/x86: sony-laptop: Drop variable assignment in sony_nc_setup_rfkill() platform/x86: sony-laptop: Fix error handling in sony_nc_setup_rfkill() tools/wmi: add a sample for dell smbios communication over WMI platform/x86: dell-smbios-wmi: introduce userspace interface platform/x86: wmi: create userspace interface for drivers platform/x86: dell-smbios: Add filtering support platform/x86: dell-smbios-smm: test for WSMT ...
This commit is contained in:
commit
07c455ee22
42 changed files with 2613 additions and 552 deletions
41
Documentation/ABI/testing/dell-smbios-wmi
Normal file
41
Documentation/ABI/testing/dell-smbios-wmi
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
What: /dev/wmi/dell-smbios
|
||||
Date: November 2017
|
||||
KernelVersion: 4.15
|
||||
Contact: "Mario Limonciello" <mario.limonciello@dell.com>
|
||||
Description:
|
||||
Perform SMBIOS calls on supported Dell machines.
|
||||
through the Dell ACPI-WMI interface.
|
||||
|
||||
IOCTL's and buffer formats are defined in:
|
||||
<uapi/linux/wmi.h>
|
||||
|
||||
1) To perform an SMBIOS call from userspace, you'll need to
|
||||
first determine the minimum size of the calling interface
|
||||
buffer for your machine.
|
||||
Platforms that contain larger buffers can return larger
|
||||
objects from the system firmware.
|
||||
Commonly this size is either 4k or 32k.
|
||||
|
||||
To determine the size of the buffer read() a u64 dword from
|
||||
the WMI character device /dev/wmi/dell-smbios.
|
||||
|
||||
2) After you've determined the minimum size of the calling
|
||||
interface buffer, you can allocate a structure that represents
|
||||
the structure documented above.
|
||||
|
||||
3) In the 'length' object store the size of the buffer you
|
||||
determined above and allocated.
|
||||
|
||||
4) In this buffer object, prepare as necessary for the SMBIOS
|
||||
call you're interested in. Typically SMBIOS buffers have
|
||||
"class", "select", and "input" defined to values that coincide
|
||||
with the data you are interested in.
|
||||
Documenting class/select/input values is outside of the scope
|
||||
of this documentation. Check with the libsmbios project for
|
||||
further documentation on these values.
|
||||
|
||||
6) Run the call by using ioctl() as described in the header.
|
||||
|
||||
7) The output will be returned in the buffer object.
|
||||
|
||||
8) Be sure to free up your allocated object.
|
||||
21
Documentation/ABI/testing/sysfs-platform-dell-smbios
Normal file
21
Documentation/ABI/testing/sysfs-platform-dell-smbios
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
What: /sys/devices/platform/<platform>/tokens/*
|
||||
Date: November 2017
|
||||
KernelVersion: 4.15
|
||||
Contact: "Mario Limonciello" <mario.limonciello@dell.com>
|
||||
Description:
|
||||
A read-only description of Dell platform tokens
|
||||
available on the machine.
|
||||
|
||||
Each token attribute is available as a pair of
|
||||
sysfs attributes readable by a process with
|
||||
CAP_SYS_ADMIN.
|
||||
|
||||
For example the token ID "5" would be available
|
||||
as the following attributes:
|
||||
|
||||
0005_location
|
||||
0005_value
|
||||
|
||||
Tokens will vary from machine to machine, and
|
||||
only tokens available on that machine will be
|
||||
displayed.
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
What: /sys/devices/platform/<platform>/force_power
|
||||
Date: September 2017
|
||||
KernelVersion: 4.15
|
||||
Contact: "Mario Limonciello" <mario.limonciello@dell.com>
|
||||
Description:
|
||||
Modify the platform force power state, influencing
|
||||
Thunderbolt controllers to turn on or off when no
|
||||
devices are connected (write-only)
|
||||
There are two available states:
|
||||
* 0 -> Force power disabled
|
||||
* 1 -> Force power enabled
|
||||
|
|
@ -221,3 +221,18 @@ The driver will create one virtual ethernet interface per Thunderbolt
|
|||
port which are named like ``thunderbolt0`` and so on. From this point
|
||||
you can either use standard userspace tools like ``ifconfig`` to
|
||||
configure the interface or let your GUI to handle it automatically.
|
||||
|
||||
Forcing power
|
||||
-------------
|
||||
Many OEMs include a method that can be used to force the power of a
|
||||
thunderbolt controller to an "On" state even if nothing is connected.
|
||||
If supported by your machine this will be exposed by the WMI bus with
|
||||
a sysfs attribute called "force_power".
|
||||
|
||||
For example the intel-wmi-thunderbolt driver exposes this attribute in:
|
||||
/sys/devices/platform/PNP0C14:00/wmi_bus/wmi_bus-PNP0C14:00/86CCFD48-205E-4A77-9C48-2021CBEDE341/force_power
|
||||
|
||||
To force the power to on, write 1 to this attribute file.
|
||||
To disable force power, write 0 to this attribute file.
|
||||
|
||||
Note: it's currently not possible to query the force power state of a platform.
|
||||
|
|
|
|||
39
MAINTAINERS
39
MAINTAINERS
|
|
@ -384,6 +384,7 @@ ACPI WMI DRIVER
|
|||
L: platform-driver-x86@vger.kernel.org
|
||||
S: Orphan
|
||||
F: drivers/platform/x86/wmi.c
|
||||
F: include/uapi/linux/wmi.h
|
||||
|
||||
AD1889 ALSA SOUND DRIVER
|
||||
M: Thibaut Varene <T-Bone@parisc-linux.org>
|
||||
|
|
@ -4030,6 +4031,26 @@ M: "Maciej W. Rozycki" <macro@linux-mips.org>
|
|||
S: Maintained
|
||||
F: drivers/net/fddi/defxx.*
|
||||
|
||||
DELL SMBIOS DRIVER
|
||||
M: Pali Rohár <pali.rohar@gmail.com>
|
||||
M: Mario Limonciello <mario.limonciello@dell.com>
|
||||
L: platform-driver-x86@vger.kernel.org
|
||||
S: Maintained
|
||||
F: drivers/platform/x86/dell-smbios.*
|
||||
|
||||
DELL SMBIOS SMM DRIVER
|
||||
M: Mario Limonciello <mario.limonciello@dell.com>
|
||||
L: platform-driver-x86@vger.kernel.org
|
||||
S: Maintained
|
||||
F: drivers/platform/x86/dell-smbios-smm.c
|
||||
|
||||
DELL SMBIOS WMI DRIVER
|
||||
M: Mario Limonciello <mario.limonciello@dell.com>
|
||||
L: platform-driver-x86@vger.kernel.org
|
||||
S: Maintained
|
||||
F: drivers/platform/x86/dell-smbios-wmi.c
|
||||
F: tools/wmi/dell-smbios-example.c
|
||||
|
||||
DELL LAPTOP DRIVER
|
||||
M: Matthew Garrett <mjg59@srcf.ucam.org>
|
||||
M: Pali Rohár <pali.rohar@gmail.com>
|
||||
|
|
@ -4059,12 +4080,17 @@ S: Maintained
|
|||
F: Documentation/dcdbas.txt
|
||||
F: drivers/firmware/dcdbas.*
|
||||
|
||||
DELL WMI EXTRAS DRIVER
|
||||
DELL WMI NOTIFICATIONS DRIVER
|
||||
M: Matthew Garrett <mjg59@srcf.ucam.org>
|
||||
M: Pali Rohár <pali.rohar@gmail.com>
|
||||
S: Maintained
|
||||
F: drivers/platform/x86/dell-wmi.c
|
||||
|
||||
DELL WMI DESCRIPTOR DRIVER
|
||||
M: Mario Limonciello <mario.limonciello@dell.com>
|
||||
S: Maintained
|
||||
F: drivers/platform/x86/dell-wmi-descriptor.c
|
||||
|
||||
DELTA ST MEDIA DRIVER
|
||||
M: Hugues Fruchet <hugues.fruchet@st.com>
|
||||
L: linux-media@vger.kernel.org
|
||||
|
|
@ -7181,6 +7207,11 @@ F: Documentation/wimax/README.i2400m
|
|||
F: drivers/net/wimax/i2400m/
|
||||
F: include/uapi/linux/wimax/i2400m.h
|
||||
|
||||
INTEL WMI THUNDERBOLT FORCE POWER DRIVER
|
||||
M: Mario Limonciello <mario.limonciello@dell.com>
|
||||
S: Maintained
|
||||
F: drivers/platform/x86/intel-wmi-thunderbolt.c
|
||||
|
||||
INTEL(R) TRACE HUB
|
||||
M: Alexander Shishkin <alexander.shishkin@linux.intel.com>
|
||||
S: Supported
|
||||
|
|
@ -10630,6 +10661,12 @@ S: Maintained
|
|||
F: crypto/pcrypt.c
|
||||
F: include/crypto/pcrypt.h
|
||||
|
||||
PEAQ WMI HOTKEYS DRIVER
|
||||
M: Hans de Goede <hdegoede@redhat.com>
|
||||
L: platform-driver-x86@vger.kernel.org
|
||||
S: Maintained
|
||||
F: drivers/platform/x86/peaq-wmi.c
|
||||
|
||||
PER-CPU MEMORY ALLOCATOR
|
||||
M: Tejun Heo <tj@kernel.org>
|
||||
M: Christoph Lameter <cl@linux.com>
|
||||
|
|
|
|||
|
|
@ -93,12 +93,33 @@ config ASUS_LAPTOP
|
|||
|
||||
config DELL_SMBIOS
|
||||
tristate
|
||||
select DCDBAS
|
||||
---help---
|
||||
This module provides common functions for kernel modules using
|
||||
Dell SMBIOS.
|
||||
|
||||
If you have a Dell laptop, say Y or M here.
|
||||
config DELL_SMBIOS_WMI
|
||||
tristate "Dell SMBIOS calling interface (WMI implementation)"
|
||||
depends on ACPI_WMI
|
||||
select DELL_WMI_DESCRIPTOR
|
||||
default ACPI_WMI
|
||||
select DELL_SMBIOS
|
||||
---help---
|
||||
This provides an implementation for the Dell SMBIOS calling interface
|
||||
communicated over ACPI-WMI.
|
||||
|
||||
If you have a Dell computer from >2007 you should say Y or M here.
|
||||
If you aren't sure and this module doesn't work for your computer
|
||||
it just won't load.
|
||||
|
||||
config DELL_SMBIOS_SMM
|
||||
tristate "Dell SMBIOS calling interface (SMM implementation)"
|
||||
depends on DCDBAS
|
||||
default DCDBAS
|
||||
select DELL_SMBIOS
|
||||
---help---
|
||||
This provides an implementation for the Dell SMBIOS calling interface
|
||||
communicated over SMI/SMM.
|
||||
|
||||
If you have a Dell computer from <=2017 you should say Y or M here.
|
||||
If you aren't sure and this module doesn't work for your computer
|
||||
it just won't load.
|
||||
|
||||
config DELL_LAPTOP
|
||||
tristate "Dell Laptop Extras"
|
||||
|
|
@ -116,11 +137,12 @@ config DELL_LAPTOP
|
|||
laptops (except for some models covered by the Compal driver).
|
||||
|
||||
config DELL_WMI
|
||||
tristate "Dell WMI extras"
|
||||
tristate "Dell WMI notifications"
|
||||
depends on ACPI_WMI
|
||||
depends on DMI
|
||||
depends on INPUT
|
||||
depends on ACPI_VIDEO || ACPI_VIDEO = n
|
||||
select DELL_WMI_DESCRIPTOR
|
||||
select DELL_SMBIOS
|
||||
select INPUT_SPARSEKMAP
|
||||
---help---
|
||||
|
|
@ -129,6 +151,10 @@ config DELL_WMI
|
|||
To compile this driver as a module, choose M here: the module will
|
||||
be called dell-wmi.
|
||||
|
||||
config DELL_WMI_DESCRIPTOR
|
||||
tristate
|
||||
depends on ACPI_WMI
|
||||
|
||||
config DELL_WMI_AIO
|
||||
tristate "WMI Hotkeys for Dell All-In-One series"
|
||||
depends on ACPI_WMI
|
||||
|
|
@ -658,6 +684,19 @@ config WMI_BMOF
|
|||
To compile this driver as a module, choose M here: the module will
|
||||
be called wmi-bmof.
|
||||
|
||||
config INTEL_WMI_THUNDERBOLT
|
||||
tristate "Intel WMI thunderbolt force power driver"
|
||||
depends on ACPI_WMI
|
||||
default ACPI_WMI
|
||||
---help---
|
||||
Say Y here if you want to be able to use the WMI interface on select
|
||||
systems to force the power control of Intel Thunderbolt controllers.
|
||||
This is useful for updating the firmware when devices are not plugged
|
||||
into the controller.
|
||||
|
||||
To compile this driver as a module, choose M here: the module will
|
||||
be called intel-wmi-thunderbolt.
|
||||
|
||||
config MSI_WMI
|
||||
tristate "MSI WMI extras"
|
||||
depends on ACPI_WMI
|
||||
|
|
@ -793,7 +832,7 @@ config ACPI_CMPC
|
|||
|
||||
config INTEL_CHT_INT33FE
|
||||
tristate "Intel Cherry Trail ACPI INT33FE Driver"
|
||||
depends on X86 && ACPI && I2C
|
||||
depends on X86 && ACPI && I2C && REGULATOR
|
||||
---help---
|
||||
This driver add support for the INT33FE ACPI device found on
|
||||
some Intel Cherry Trail devices.
|
||||
|
|
@ -804,6 +843,10 @@ config INTEL_CHT_INT33FE
|
|||
This driver instantiates i2c-clients for these, so that standard
|
||||
i2c drivers for these chips can bind to the them.
|
||||
|
||||
If you enable this driver it is advised to also select
|
||||
CONFIG_TYPEC_FUSB302=m, CONFIG_CHARGER_BQ24190=m and
|
||||
CONFIG_BATTERY_MAX17042=m.
|
||||
|
||||
config INTEL_INT0002_VGPIO
|
||||
tristate "Intel ACPI INT0002 Virtual GPIO driver"
|
||||
depends on GPIOLIB && ACPI
|
||||
|
|
@ -1088,7 +1131,6 @@ config INTEL_PUNIT_IPC
|
|||
|
||||
config INTEL_TELEMETRY
|
||||
tristate "Intel SoC Telemetry Driver"
|
||||
default n
|
||||
depends on INTEL_PMC_IPC && INTEL_PUNIT_IPC && X86_64
|
||||
---help---
|
||||
This driver provides interfaces to configure and use
|
||||
|
|
|
|||
|
|
@ -13,8 +13,11 @@ obj-$(CONFIG_MSI_LAPTOP) += msi-laptop.o
|
|||
obj-$(CONFIG_ACPI_CMPC) += classmate-laptop.o
|
||||
obj-$(CONFIG_COMPAL_LAPTOP) += compal-laptop.o
|
||||
obj-$(CONFIG_DELL_SMBIOS) += dell-smbios.o
|
||||
obj-$(CONFIG_DELL_SMBIOS_WMI) += dell-smbios-wmi.o
|
||||
obj-$(CONFIG_DELL_SMBIOS_SMM) += dell-smbios-smm.o
|
||||
obj-$(CONFIG_DELL_LAPTOP) += dell-laptop.o
|
||||
obj-$(CONFIG_DELL_WMI) += dell-wmi.o
|
||||
obj-$(CONFIG_DELL_WMI_DESCRIPTOR) += dell-wmi-descriptor.o
|
||||
obj-$(CONFIG_DELL_WMI_AIO) += dell-wmi-aio.o
|
||||
obj-$(CONFIG_DELL_WMI_LED) += dell-wmi-led.o
|
||||
obj-$(CONFIG_DELL_SMO8800) += dell-smo8800.o
|
||||
|
|
@ -40,6 +43,7 @@ obj-$(CONFIG_PEAQ_WMI) += peaq-wmi.o
|
|||
obj-$(CONFIG_SURFACE3_WMI) += surface3-wmi.o
|
||||
obj-$(CONFIG_TOPSTAR_LAPTOP) += topstar-laptop.o
|
||||
obj-$(CONFIG_WMI_BMOF) += wmi-bmof.o
|
||||
obj-$(CONFIG_INTEL_WMI_THUNDERBOLT) += intel-wmi-thunderbolt.o
|
||||
|
||||
# toshiba_acpi must link after wmi to ensure that wmi devices are found
|
||||
# before toshiba_acpi initializes
|
||||
|
|
|
|||
|
|
@ -119,6 +119,7 @@ MODULE_LICENSE("GPL");
|
|||
#define ASUS_WMI_DEVID_BRIGHTNESS 0x00050012
|
||||
#define ASUS_WMI_DEVID_KBD_BACKLIGHT 0x00050021
|
||||
#define ASUS_WMI_DEVID_LIGHT_SENSOR 0x00050022 /* ?? */
|
||||
#define ASUS_WMI_DEVID_LIGHTBAR 0x00050025
|
||||
|
||||
/* Misc */
|
||||
#define ASUS_WMI_DEVID_CAMERA 0x00060013
|
||||
|
|
@ -148,6 +149,7 @@ MODULE_LICENSE("GPL");
|
|||
#define ASUS_WMI_DSTS_BIOS_BIT 0x00040000
|
||||
#define ASUS_WMI_DSTS_BRIGHTNESS_MASK 0x000000FF
|
||||
#define ASUS_WMI_DSTS_MAX_BRIGTH_MASK 0x0000FF00
|
||||
#define ASUS_WMI_DSTS_LIGHTBAR_MASK 0x0000000F
|
||||
|
||||
#define ASUS_FAN_DESC "cpu_fan"
|
||||
#define ASUS_FAN_MFUN 0x13
|
||||
|
|
@ -222,10 +224,13 @@ struct asus_wmi {
|
|||
int tpd_led_wk;
|
||||
struct led_classdev kbd_led;
|
||||
int kbd_led_wk;
|
||||
struct led_classdev lightbar_led;
|
||||
int lightbar_led_wk;
|
||||
struct workqueue_struct *led_workqueue;
|
||||
struct work_struct tpd_led_work;
|
||||
struct work_struct kbd_led_work;
|
||||
struct work_struct wlan_led_work;
|
||||
struct work_struct lightbar_led_work;
|
||||
|
||||
struct asus_rfkill wlan;
|
||||
struct asus_rfkill bluetooth;
|
||||
|
|
@ -567,6 +572,48 @@ static enum led_brightness wlan_led_get(struct led_classdev *led_cdev)
|
|||
return result & ASUS_WMI_DSTS_BRIGHTNESS_MASK;
|
||||
}
|
||||
|
||||
static void lightbar_led_update(struct work_struct *work)
|
||||
{
|
||||
struct asus_wmi *asus;
|
||||
int ctrl_param;
|
||||
|
||||
asus = container_of(work, struct asus_wmi, lightbar_led_work);
|
||||
|
||||
ctrl_param = asus->lightbar_led_wk;
|
||||
asus_wmi_set_devstate(ASUS_WMI_DEVID_LIGHTBAR, ctrl_param, NULL);
|
||||
}
|
||||
|
||||
static void lightbar_led_set(struct led_classdev *led_cdev,
|
||||
enum led_brightness value)
|
||||
{
|
||||
struct asus_wmi *asus;
|
||||
|
||||
asus = container_of(led_cdev, struct asus_wmi, lightbar_led);
|
||||
|
||||
asus->lightbar_led_wk = !!value;
|
||||
queue_work(asus->led_workqueue, &asus->lightbar_led_work);
|
||||
}
|
||||
|
||||
static enum led_brightness lightbar_led_get(struct led_classdev *led_cdev)
|
||||
{
|
||||
struct asus_wmi *asus;
|
||||
u32 result;
|
||||
|
||||
asus = container_of(led_cdev, struct asus_wmi, lightbar_led);
|
||||
asus_wmi_get_devstate(asus, ASUS_WMI_DEVID_LIGHTBAR, &result);
|
||||
|
||||
return result & ASUS_WMI_DSTS_LIGHTBAR_MASK;
|
||||
}
|
||||
|
||||
static int lightbar_led_presence(struct asus_wmi *asus)
|
||||
{
|
||||
u32 result;
|
||||
|
||||
asus_wmi_get_devstate(asus, ASUS_WMI_DEVID_LIGHTBAR, &result);
|
||||
|
||||
return result & ASUS_WMI_DSTS_PRESENCE_BIT;
|
||||
}
|
||||
|
||||
static void asus_wmi_led_exit(struct asus_wmi *asus)
|
||||
{
|
||||
if (!IS_ERR_OR_NULL(asus->kbd_led.dev))
|
||||
|
|
@ -575,6 +622,8 @@ static void asus_wmi_led_exit(struct asus_wmi *asus)
|
|||
led_classdev_unregister(&asus->tpd_led);
|
||||
if (!IS_ERR_OR_NULL(asus->wlan_led.dev))
|
||||
led_classdev_unregister(&asus->wlan_led);
|
||||
if (!IS_ERR_OR_NULL(asus->lightbar_led.dev))
|
||||
led_classdev_unregister(&asus->lightbar_led);
|
||||
if (asus->led_workqueue)
|
||||
destroy_workqueue(asus->led_workqueue);
|
||||
}
|
||||
|
|
@ -630,6 +679,20 @@ static int asus_wmi_led_init(struct asus_wmi *asus)
|
|||
|
||||
rv = led_classdev_register(&asus->platform_device->dev,
|
||||
&asus->wlan_led);
|
||||
if (rv)
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (lightbar_led_presence(asus)) {
|
||||
INIT_WORK(&asus->lightbar_led_work, lightbar_led_update);
|
||||
|
||||
asus->lightbar_led.name = "asus::lightbar";
|
||||
asus->lightbar_led.brightness_set = lightbar_led_set;
|
||||
asus->lightbar_led.brightness_get = lightbar_led_get;
|
||||
asus->lightbar_led.max_brightness = 1;
|
||||
|
||||
rv = led_classdev_register(&asus->platform_device->dev,
|
||||
&asus->lightbar_led);
|
||||
}
|
||||
|
||||
error:
|
||||
|
|
|
|||
|
|
@ -35,18 +35,6 @@
|
|||
#include "dell-rbtn.h"
|
||||
#include "dell-smbios.h"
|
||||
|
||||
#define BRIGHTNESS_TOKEN 0x7d
|
||||
#define KBD_LED_OFF_TOKEN 0x01E1
|
||||
#define KBD_LED_ON_TOKEN 0x01E2
|
||||
#define KBD_LED_AUTO_TOKEN 0x01E3
|
||||
#define KBD_LED_AUTO_25_TOKEN 0x02EA
|
||||
#define KBD_LED_AUTO_50_TOKEN 0x02EB
|
||||
#define KBD_LED_AUTO_75_TOKEN 0x02EC
|
||||
#define KBD_LED_AUTO_100_TOKEN 0x02F6
|
||||
#define GLOBAL_MIC_MUTE_ENABLE 0x0364
|
||||
#define GLOBAL_MIC_MUTE_DISABLE 0x0365
|
||||
#define KBD_LED_AC_TOKEN 0x0451
|
||||
|
||||
struct quirk_entry {
|
||||
u8 touchpad_led;
|
||||
|
||||
|
|
@ -85,6 +73,7 @@ static struct platform_driver platform_driver = {
|
|||
}
|
||||
};
|
||||
|
||||
static struct calling_interface_buffer *buffer;
|
||||
static struct platform_device *platform_device;
|
||||
static struct backlight_device *dell_backlight_device;
|
||||
static struct rfkill *wifi_rfkill;
|
||||
|
|
@ -283,6 +272,27 @@ static const struct dmi_system_id dell_quirks[] __initconst = {
|
|||
{ }
|
||||
};
|
||||
|
||||
void dell_set_arguments(u32 arg0, u32 arg1, u32 arg2, u32 arg3)
|
||||
{
|
||||
memset(buffer, 0, sizeof(struct calling_interface_buffer));
|
||||
buffer->input[0] = arg0;
|
||||
buffer->input[1] = arg1;
|
||||
buffer->input[2] = arg2;
|
||||
buffer->input[3] = arg3;
|
||||
}
|
||||
|
||||
int dell_send_request(u16 class, u16 select)
|
||||
{
|
||||
int ret;
|
||||
|
||||
buffer->cmd_class = class;
|
||||
buffer->cmd_select = select;
|
||||
ret = dell_smbios_call(buffer);
|
||||
if (ret != 0)
|
||||
return ret;
|
||||
return dell_smbios_error(buffer->output[0]);
|
||||
}
|
||||
|
||||
/*
|
||||
* Derived from information in smbios-wireless-ctl:
|
||||
*
|
||||
|
|
@ -405,7 +415,6 @@ static const struct dmi_system_id dell_quirks[] __initconst = {
|
|||
|
||||
static int dell_rfkill_set(void *data, bool blocked)
|
||||
{
|
||||
struct calling_interface_buffer *buffer;
|
||||
int disable = blocked ? 1 : 0;
|
||||
unsigned long radio = (unsigned long)data;
|
||||
int hwswitch_bit = (unsigned long)data - 1;
|
||||
|
|
@ -413,20 +422,16 @@ static int dell_rfkill_set(void *data, bool blocked)
|
|||
int status;
|
||||
int ret;
|
||||
|
||||
buffer = dell_smbios_get_buffer();
|
||||
|
||||
dell_smbios_send_request(17, 11);
|
||||
ret = buffer->output[0];
|
||||
dell_set_arguments(0, 0, 0, 0);
|
||||
ret = dell_send_request(CLASS_INFO, SELECT_RFKILL);
|
||||
if (ret)
|
||||
return ret;
|
||||
status = buffer->output[1];
|
||||
|
||||
if (ret != 0)
|
||||
goto out;
|
||||
|
||||
dell_smbios_clear_buffer();
|
||||
|
||||
buffer->input[0] = 0x2;
|
||||
dell_smbios_send_request(17, 11);
|
||||
ret = buffer->output[0];
|
||||
dell_set_arguments(0x2, 0, 0, 0);
|
||||
ret = dell_send_request(CLASS_INFO, SELECT_RFKILL);
|
||||
if (ret)
|
||||
return ret;
|
||||
hwswitch = buffer->output[1];
|
||||
|
||||
/* If the hardware switch controls this radio, and the hardware
|
||||
|
|
@ -435,28 +440,19 @@ static int dell_rfkill_set(void *data, bool blocked)
|
|||
(status & BIT(0)) && !(status & BIT(16)))
|
||||
disable = 1;
|
||||
|
||||
dell_smbios_clear_buffer();
|
||||
|
||||
buffer->input[0] = (1 | (radio<<8) | (disable << 16));
|
||||
dell_smbios_send_request(17, 11);
|
||||
ret = buffer->output[0];
|
||||
|
||||
out:
|
||||
dell_smbios_release_buffer();
|
||||
return dell_smbios_error(ret);
|
||||
dell_set_arguments(1 | (radio<<8) | (disable << 16), 0, 0, 0);
|
||||
ret = dell_send_request(CLASS_INFO, SELECT_RFKILL);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Must be called with the buffer held */
|
||||
static void dell_rfkill_update_sw_state(struct rfkill *rfkill, int radio,
|
||||
int status,
|
||||
struct calling_interface_buffer *buffer)
|
||||
int status)
|
||||
{
|
||||
if (status & BIT(0)) {
|
||||
/* Has hw-switch, sync sw_state to BIOS */
|
||||
int block = rfkill_blocked(rfkill);
|
||||
dell_smbios_clear_buffer();
|
||||
buffer->input[0] = (1 | (radio << 8) | (block << 16));
|
||||
dell_smbios_send_request(17, 11);
|
||||
dell_set_arguments(1 | (radio << 8) | (block << 16), 0, 0, 0);
|
||||
dell_send_request(CLASS_INFO, SELECT_RFKILL);
|
||||
} else {
|
||||
/* No hw-switch, sync BIOS state to sw_state */
|
||||
rfkill_set_sw_state(rfkill, !!(status & BIT(radio + 16)));
|
||||
|
|
@ -472,32 +468,23 @@ static void dell_rfkill_update_hw_state(struct rfkill *rfkill, int radio,
|
|||
|
||||
static void dell_rfkill_query(struct rfkill *rfkill, void *data)
|
||||
{
|
||||
struct calling_interface_buffer *buffer;
|
||||
int radio = ((unsigned long)data & 0xF);
|
||||
int hwswitch;
|
||||
int status;
|
||||
int ret;
|
||||
|
||||
buffer = dell_smbios_get_buffer();
|
||||
|
||||
dell_smbios_send_request(17, 11);
|
||||
ret = buffer->output[0];
|
||||
dell_set_arguments(0, 0, 0, 0);
|
||||
ret = dell_send_request(CLASS_INFO, SELECT_RFKILL);
|
||||
status = buffer->output[1];
|
||||
|
||||
if (ret != 0 || !(status & BIT(0))) {
|
||||
dell_smbios_release_buffer();
|
||||
return;
|
||||
}
|
||||
|
||||
dell_smbios_clear_buffer();
|
||||
|
||||
buffer->input[0] = 0x2;
|
||||
dell_smbios_send_request(17, 11);
|
||||
ret = buffer->output[0];
|
||||
dell_set_arguments(0, 0x2, 0, 0);
|
||||
ret = dell_send_request(CLASS_INFO, SELECT_RFKILL);
|
||||
hwswitch = buffer->output[1];
|
||||
|
||||
dell_smbios_release_buffer();
|
||||
|
||||
if (ret != 0)
|
||||
return;
|
||||
|
||||
|
|
@ -513,27 +500,23 @@ static struct dentry *dell_laptop_dir;
|
|||
|
||||
static int dell_debugfs_show(struct seq_file *s, void *data)
|
||||
{
|
||||
struct calling_interface_buffer *buffer;
|
||||
int hwswitch_state;
|
||||
int hwswitch_ret;
|
||||
int status;
|
||||
int ret;
|
||||
|
||||
buffer = dell_smbios_get_buffer();
|
||||
|
||||
dell_smbios_send_request(17, 11);
|
||||
ret = buffer->output[0];
|
||||
dell_set_arguments(0, 0, 0, 0);
|
||||
ret = dell_send_request(CLASS_INFO, SELECT_RFKILL);
|
||||
if (ret)
|
||||
return ret;
|
||||
status = buffer->output[1];
|
||||
|
||||
dell_smbios_clear_buffer();
|
||||
|
||||
buffer->input[0] = 0x2;
|
||||
dell_smbios_send_request(17, 11);
|
||||
hwswitch_ret = buffer->output[0];
|
||||
dell_set_arguments(0, 0x2, 0, 0);
|
||||
hwswitch_ret = dell_send_request(CLASS_INFO, SELECT_RFKILL);
|
||||
if (hwswitch_ret)
|
||||
return hwswitch_ret;
|
||||
hwswitch_state = buffer->output[1];
|
||||
|
||||
dell_smbios_release_buffer();
|
||||
|
||||
seq_printf(s, "return:\t%d\n", ret);
|
||||
seq_printf(s, "status:\t0x%X\n", status);
|
||||
seq_printf(s, "Bit 0 : Hardware switch supported: %lu\n",
|
||||
|
|
@ -613,46 +596,36 @@ static const struct file_operations dell_debugfs_fops = {
|
|||
|
||||
static void dell_update_rfkill(struct work_struct *ignored)
|
||||
{
|
||||
struct calling_interface_buffer *buffer;
|
||||
int hwswitch = 0;
|
||||
int status;
|
||||
int ret;
|
||||
|
||||
buffer = dell_smbios_get_buffer();
|
||||
|
||||
dell_smbios_send_request(17, 11);
|
||||
ret = buffer->output[0];
|
||||
dell_set_arguments(0, 0, 0, 0);
|
||||
ret = dell_send_request(CLASS_INFO, SELECT_RFKILL);
|
||||
status = buffer->output[1];
|
||||
|
||||
if (ret != 0)
|
||||
goto out;
|
||||
return;
|
||||
|
||||
dell_smbios_clear_buffer();
|
||||
|
||||
buffer->input[0] = 0x2;
|
||||
dell_smbios_send_request(17, 11);
|
||||
ret = buffer->output[0];
|
||||
dell_set_arguments(0, 0x2, 0, 0);
|
||||
ret = dell_send_request(CLASS_INFO, SELECT_RFKILL);
|
||||
|
||||
if (ret == 0 && (status & BIT(0)))
|
||||
hwswitch = buffer->output[1];
|
||||
|
||||
if (wifi_rfkill) {
|
||||
dell_rfkill_update_hw_state(wifi_rfkill, 1, status, hwswitch);
|
||||
dell_rfkill_update_sw_state(wifi_rfkill, 1, status, buffer);
|
||||
dell_rfkill_update_sw_state(wifi_rfkill, 1, status);
|
||||
}
|
||||
if (bluetooth_rfkill) {
|
||||
dell_rfkill_update_hw_state(bluetooth_rfkill, 2, status,
|
||||
hwswitch);
|
||||
dell_rfkill_update_sw_state(bluetooth_rfkill, 2, status,
|
||||
buffer);
|
||||
dell_rfkill_update_sw_state(bluetooth_rfkill, 2, status);
|
||||
}
|
||||
if (wwan_rfkill) {
|
||||
dell_rfkill_update_hw_state(wwan_rfkill, 3, status, hwswitch);
|
||||
dell_rfkill_update_sw_state(wwan_rfkill, 3, status, buffer);
|
||||
dell_rfkill_update_sw_state(wwan_rfkill, 3, status);
|
||||
}
|
||||
|
||||
out:
|
||||
dell_smbios_release_buffer();
|
||||
}
|
||||
static DECLARE_DELAYED_WORK(dell_rfkill_work, dell_update_rfkill);
|
||||
|
||||
|
|
@ -696,7 +669,6 @@ static struct notifier_block dell_laptop_rbtn_notifier = {
|
|||
|
||||
static int __init dell_setup_rfkill(void)
|
||||
{
|
||||
struct calling_interface_buffer *buffer;
|
||||
int status, ret, whitelisted;
|
||||
const char *product;
|
||||
|
||||
|
|
@ -712,11 +684,9 @@ static int __init dell_setup_rfkill(void)
|
|||
if (!force_rfkill && !whitelisted)
|
||||
return 0;
|
||||
|
||||
buffer = dell_smbios_get_buffer();
|
||||
dell_smbios_send_request(17, 11);
|
||||
ret = buffer->output[0];
|
||||
dell_set_arguments(0, 0, 0, 0);
|
||||
ret = dell_send_request(CLASS_INFO, SELECT_RFKILL);
|
||||
status = buffer->output[1];
|
||||
dell_smbios_release_buffer();
|
||||
|
||||
/* dell wireless info smbios call is not supported */
|
||||
if (ret != 0)
|
||||
|
|
@ -869,7 +839,6 @@ static void dell_cleanup_rfkill(void)
|
|||
|
||||
static int dell_send_intensity(struct backlight_device *bd)
|
||||
{
|
||||
struct calling_interface_buffer *buffer;
|
||||
struct calling_interface_token *token;
|
||||
int ret;
|
||||
|
||||
|
|
@ -877,24 +846,17 @@ static int dell_send_intensity(struct backlight_device *bd)
|
|||
if (!token)
|
||||
return -ENODEV;
|
||||
|
||||
buffer = dell_smbios_get_buffer();
|
||||
buffer->input[0] = token->location;
|
||||
buffer->input[1] = bd->props.brightness;
|
||||
|
||||
dell_set_arguments(token->location, bd->props.brightness, 0, 0);
|
||||
if (power_supply_is_system_supplied() > 0)
|
||||
dell_smbios_send_request(1, 2);
|
||||
ret = dell_send_request(CLASS_TOKEN_WRITE, SELECT_TOKEN_AC);
|
||||
else
|
||||
dell_smbios_send_request(1, 1);
|
||||
ret = dell_send_request(CLASS_TOKEN_WRITE, SELECT_TOKEN_BAT);
|
||||
|
||||
ret = dell_smbios_error(buffer->output[0]);
|
||||
|
||||
dell_smbios_release_buffer();
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int dell_get_intensity(struct backlight_device *bd)
|
||||
{
|
||||
struct calling_interface_buffer *buffer;
|
||||
struct calling_interface_token *token;
|
||||
int ret;
|
||||
|
||||
|
|
@ -902,20 +864,14 @@ static int dell_get_intensity(struct backlight_device *bd)
|
|||
if (!token)
|
||||
return -ENODEV;
|
||||
|
||||
buffer = dell_smbios_get_buffer();
|
||||
buffer->input[0] = token->location;
|
||||
|
||||
dell_set_arguments(token->location, 0, 0, 0);
|
||||
if (power_supply_is_system_supplied() > 0)
|
||||
dell_smbios_send_request(0, 2);
|
||||
ret = dell_send_request(CLASS_TOKEN_READ, SELECT_TOKEN_AC);
|
||||
else
|
||||
dell_smbios_send_request(0, 1);
|
||||
ret = dell_send_request(CLASS_TOKEN_READ, SELECT_TOKEN_BAT);
|
||||
|
||||
if (buffer->output[0])
|
||||
ret = dell_smbios_error(buffer->output[0]);
|
||||
else
|
||||
if (ret == 0)
|
||||
ret = buffer->output[1];
|
||||
|
||||
dell_smbios_release_buffer();
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
|
@ -1179,20 +1135,13 @@ static DEFINE_MUTEX(kbd_led_mutex);
|
|||
|
||||
static int kbd_get_info(struct kbd_info *info)
|
||||
{
|
||||
struct calling_interface_buffer *buffer;
|
||||
u8 units;
|
||||
int ret;
|
||||
|
||||
buffer = dell_smbios_get_buffer();
|
||||
|
||||
buffer->input[0] = 0x0;
|
||||
dell_smbios_send_request(4, 11);
|
||||
ret = buffer->output[0];
|
||||
|
||||
if (ret) {
|
||||
ret = dell_smbios_error(ret);
|
||||
goto out;
|
||||
}
|
||||
dell_set_arguments(0, 0, 0, 0);
|
||||
ret = dell_send_request(CLASS_KBD_BACKLIGHT, SELECT_KBD_BACKLIGHT);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
info->modes = buffer->output[1] & 0xFFFF;
|
||||
info->type = (buffer->output[1] >> 24) & 0xFF;
|
||||
|
|
@ -1209,8 +1158,6 @@ static int kbd_get_info(struct kbd_info *info)
|
|||
if (units & BIT(3))
|
||||
info->days = (buffer->output[3] >> 24) & 0xFF;
|
||||
|
||||
out:
|
||||
dell_smbios_release_buffer();
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
|
@ -1269,19 +1216,12 @@ static int kbd_set_level(struct kbd_state *state, u8 level)
|
|||
|
||||
static int kbd_get_state(struct kbd_state *state)
|
||||
{
|
||||
struct calling_interface_buffer *buffer;
|
||||
int ret;
|
||||
|
||||
buffer = dell_smbios_get_buffer();
|
||||
|
||||
buffer->input[0] = 0x1;
|
||||
dell_smbios_send_request(4, 11);
|
||||
ret = buffer->output[0];
|
||||
|
||||
if (ret) {
|
||||
ret = dell_smbios_error(ret);
|
||||
goto out;
|
||||
}
|
||||
dell_set_arguments(0x1, 0, 0, 0);
|
||||
ret = dell_send_request(CLASS_KBD_BACKLIGHT, SELECT_KBD_BACKLIGHT);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
state->mode_bit = ffs(buffer->output[1] & 0xFFFF);
|
||||
if (state->mode_bit != 0)
|
||||
|
|
@ -1296,31 +1236,27 @@ static int kbd_get_state(struct kbd_state *state)
|
|||
state->timeout_value_ac = (buffer->output[2] >> 24) & 0x3F;
|
||||
state->timeout_unit_ac = (buffer->output[2] >> 30) & 0x3;
|
||||
|
||||
out:
|
||||
dell_smbios_release_buffer();
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int kbd_set_state(struct kbd_state *state)
|
||||
{
|
||||
struct calling_interface_buffer *buffer;
|
||||
int ret;
|
||||
u32 input1;
|
||||
u32 input2;
|
||||
|
||||
buffer = dell_smbios_get_buffer();
|
||||
buffer->input[0] = 0x2;
|
||||
buffer->input[1] = BIT(state->mode_bit) & 0xFFFF;
|
||||
buffer->input[1] |= (state->triggers & 0xFF) << 16;
|
||||
buffer->input[1] |= (state->timeout_value & 0x3F) << 24;
|
||||
buffer->input[1] |= (state->timeout_unit & 0x3) << 30;
|
||||
buffer->input[2] = state->als_setting & 0xFF;
|
||||
buffer->input[2] |= (state->level & 0xFF) << 16;
|
||||
buffer->input[2] |= (state->timeout_value_ac & 0x3F) << 24;
|
||||
buffer->input[2] |= (state->timeout_unit_ac & 0x3) << 30;
|
||||
dell_smbios_send_request(4, 11);
|
||||
ret = buffer->output[0];
|
||||
dell_smbios_release_buffer();
|
||||
input1 = BIT(state->mode_bit) & 0xFFFF;
|
||||
input1 |= (state->triggers & 0xFF) << 16;
|
||||
input1 |= (state->timeout_value & 0x3F) << 24;
|
||||
input1 |= (state->timeout_unit & 0x3) << 30;
|
||||
input2 = state->als_setting & 0xFF;
|
||||
input2 |= (state->level & 0xFF) << 16;
|
||||
input2 |= (state->timeout_value_ac & 0x3F) << 24;
|
||||
input2 |= (state->timeout_unit_ac & 0x3) << 30;
|
||||
dell_set_arguments(0x2, input1, input2, 0);
|
||||
ret = dell_send_request(CLASS_KBD_BACKLIGHT, SELECT_KBD_BACKLIGHT);
|
||||
|
||||
return dell_smbios_error(ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int kbd_set_state_safe(struct kbd_state *state, struct kbd_state *old)
|
||||
|
|
@ -1345,7 +1281,6 @@ static int kbd_set_state_safe(struct kbd_state *state, struct kbd_state *old)
|
|||
|
||||
static int kbd_set_token_bit(u8 bit)
|
||||
{
|
||||
struct calling_interface_buffer *buffer;
|
||||
struct calling_interface_token *token;
|
||||
int ret;
|
||||
|
||||
|
|
@ -1356,19 +1291,14 @@ static int kbd_set_token_bit(u8 bit)
|
|||
if (!token)
|
||||
return -EINVAL;
|
||||
|
||||
buffer = dell_smbios_get_buffer();
|
||||
buffer->input[0] = token->location;
|
||||
buffer->input[1] = token->value;
|
||||
dell_smbios_send_request(1, 0);
|
||||
ret = buffer->output[0];
|
||||
dell_smbios_release_buffer();
|
||||
dell_set_arguments(token->location, token->value, 0, 0);
|
||||
ret = dell_send_request(CLASS_TOKEN_WRITE, SELECT_TOKEN_STD);
|
||||
|
||||
return dell_smbios_error(ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int kbd_get_token_bit(u8 bit)
|
||||
{
|
||||
struct calling_interface_buffer *buffer;
|
||||
struct calling_interface_token *token;
|
||||
int ret;
|
||||
int val;
|
||||
|
|
@ -1380,15 +1310,12 @@ static int kbd_get_token_bit(u8 bit)
|
|||
if (!token)
|
||||
return -EINVAL;
|
||||
|
||||
buffer = dell_smbios_get_buffer();
|
||||
buffer->input[0] = token->location;
|
||||
dell_smbios_send_request(0, 0);
|
||||
ret = buffer->output[0];
|
||||
dell_set_arguments(token->location, 0, 0, 0);
|
||||
ret = dell_send_request(CLASS_TOKEN_READ, SELECT_TOKEN_STD);
|
||||
val = buffer->output[1];
|
||||
dell_smbios_release_buffer();
|
||||
|
||||
if (ret)
|
||||
return dell_smbios_error(ret);
|
||||
return ret;
|
||||
|
||||
return (val == token->value);
|
||||
}
|
||||
|
|
@ -2102,7 +2029,6 @@ static struct notifier_block dell_laptop_notifier = {
|
|||
|
||||
int dell_micmute_led_set(int state)
|
||||
{
|
||||
struct calling_interface_buffer *buffer;
|
||||
struct calling_interface_token *token;
|
||||
|
||||
if (state == 0)
|
||||
|
|
@ -2115,11 +2041,8 @@ int dell_micmute_led_set(int state)
|
|||
if (!token)
|
||||
return -ENODEV;
|
||||
|
||||
buffer = dell_smbios_get_buffer();
|
||||
buffer->input[0] = token->location;
|
||||
buffer->input[1] = token->value;
|
||||
dell_smbios_send_request(1, 0);
|
||||
dell_smbios_release_buffer();
|
||||
dell_set_arguments(token->location, token->value, 0, 0);
|
||||
dell_send_request(CLASS_TOKEN_WRITE, SELECT_TOKEN_STD);
|
||||
|
||||
return state;
|
||||
}
|
||||
|
|
@ -2127,7 +2050,6 @@ EXPORT_SYMBOL_GPL(dell_micmute_led_set);
|
|||
|
||||
static int __init dell_init(void)
|
||||
{
|
||||
struct calling_interface_buffer *buffer;
|
||||
struct calling_interface_token *token;
|
||||
int max_intensity = 0;
|
||||
int ret;
|
||||
|
|
@ -2151,6 +2073,11 @@ static int __init dell_init(void)
|
|||
if (ret)
|
||||
goto fail_platform_device2;
|
||||
|
||||
buffer = kzalloc(sizeof(struct calling_interface_buffer), GFP_KERNEL);
|
||||
if (!buffer)
|
||||
goto fail_buffer;
|
||||
|
||||
|
||||
ret = dell_setup_rfkill();
|
||||
|
||||
if (ret) {
|
||||
|
|
@ -2175,12 +2102,10 @@ static int __init dell_init(void)
|
|||
|
||||
token = dell_smbios_find_token(BRIGHTNESS_TOKEN);
|
||||
if (token) {
|
||||
buffer = dell_smbios_get_buffer();
|
||||
buffer->input[0] = token->location;
|
||||
dell_smbios_send_request(0, 2);
|
||||
if (buffer->output[0] == 0)
|
||||
dell_set_arguments(token->location, 0, 0, 0);
|
||||
ret = dell_send_request(CLASS_TOKEN_READ, SELECT_TOKEN_AC);
|
||||
if (ret)
|
||||
max_intensity = buffer->output[3];
|
||||
dell_smbios_release_buffer();
|
||||
}
|
||||
|
||||
if (max_intensity) {
|
||||
|
|
@ -2214,6 +2139,8 @@ static int __init dell_init(void)
|
|||
fail_get_brightness:
|
||||
backlight_device_unregister(dell_backlight_device);
|
||||
fail_backlight:
|
||||
kfree(buffer);
|
||||
fail_buffer:
|
||||
dell_cleanup_rfkill();
|
||||
fail_rfkill:
|
||||
platform_device_del(platform_device);
|
||||
|
|
@ -2233,6 +2160,7 @@ static void __exit dell_exit(void)
|
|||
touchpad_led_exit();
|
||||
kbd_led_exit();
|
||||
backlight_device_unregister(dell_backlight_device);
|
||||
kfree(buffer);
|
||||
dell_cleanup_rfkill();
|
||||
if (platform_device) {
|
||||
platform_device_unregister(platform_device);
|
||||
|
|
|
|||
196
drivers/platform/x86/dell-smbios-smm.c
Normal file
196
drivers/platform/x86/dell-smbios-smm.c
Normal file
|
|
@ -0,0 +1,196 @@
|
|||
/*
|
||||
* SMI methods for use with dell-smbios
|
||||
*
|
||||
* Copyright (c) Red Hat <mjg@redhat.com>
|
||||
* Copyright (c) 2014 Gabriele Mazzotta <gabriele.mzt@gmail.com>
|
||||
* Copyright (c) 2014 Pali Rohár <pali.rohar@gmail.com>
|
||||
* Copyright (c) 2017 Dell Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||
|
||||
#include <linux/dmi.h>
|
||||
#include <linux/gfp.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include "../../firmware/dcdbas.h"
|
||||
#include "dell-smbios.h"
|
||||
|
||||
static int da_command_address;
|
||||
static int da_command_code;
|
||||
static struct calling_interface_buffer *buffer;
|
||||
struct platform_device *platform_device;
|
||||
static DEFINE_MUTEX(smm_mutex);
|
||||
|
||||
static const struct dmi_system_id dell_device_table[] __initconst = {
|
||||
{
|
||||
.ident = "Dell laptop",
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
|
||||
DMI_MATCH(DMI_CHASSIS_TYPE, "8"),
|
||||
},
|
||||
},
|
||||
{
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
|
||||
DMI_MATCH(DMI_CHASSIS_TYPE, "9"), /*Laptop*/
|
||||
},
|
||||
},
|
||||
{
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
|
||||
DMI_MATCH(DMI_CHASSIS_TYPE, "10"), /*Notebook*/
|
||||
},
|
||||
},
|
||||
{
|
||||
.ident = "Dell Computer Corporation",
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR, "Dell Computer Corporation"),
|
||||
DMI_MATCH(DMI_CHASSIS_TYPE, "8"),
|
||||
},
|
||||
},
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(dmi, dell_device_table);
|
||||
|
||||
static void __init parse_da_table(const struct dmi_header *dm)
|
||||
{
|
||||
struct calling_interface_structure *table =
|
||||
container_of(dm, struct calling_interface_structure, header);
|
||||
|
||||
/* 4 bytes of table header, plus 7 bytes of Dell header, plus at least
|
||||
* 6 bytes of entry
|
||||
*/
|
||||
if (dm->length < 17)
|
||||
return;
|
||||
|
||||
da_command_address = table->cmdIOAddress;
|
||||
da_command_code = table->cmdIOCode;
|
||||
}
|
||||
|
||||
static void __init find_cmd_address(const struct dmi_header *dm, void *dummy)
|
||||
{
|
||||
switch (dm->type) {
|
||||
case 0xda: /* Calling interface */
|
||||
parse_da_table(dm);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
int dell_smbios_smm_call(struct calling_interface_buffer *input)
|
||||
{
|
||||
struct smi_cmd command;
|
||||
size_t size;
|
||||
|
||||
size = sizeof(struct calling_interface_buffer);
|
||||
command.magic = SMI_CMD_MAGIC;
|
||||
command.command_address = da_command_address;
|
||||
command.command_code = da_command_code;
|
||||
command.ebx = virt_to_phys(buffer);
|
||||
command.ecx = 0x42534931;
|
||||
|
||||
mutex_lock(&smm_mutex);
|
||||
memcpy(buffer, input, size);
|
||||
dcdbas_smi_request(&command);
|
||||
memcpy(input, buffer, size);
|
||||
mutex_unlock(&smm_mutex);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* When enabled this indicates that SMM won't work */
|
||||
static bool test_wsmt_enabled(void)
|
||||
{
|
||||
struct calling_interface_token *wsmt;
|
||||
|
||||
/* if token doesn't exist, SMM will work */
|
||||
wsmt = dell_smbios_find_token(WSMT_EN_TOKEN);
|
||||
if (!wsmt)
|
||||
return false;
|
||||
|
||||
/* If token exists, try to access over SMM but set a dummy return.
|
||||
* - If WSMT disabled it will be overwritten by SMM
|
||||
* - If WSMT enabled then dummy value will remain
|
||||
*/
|
||||
buffer->cmd_class = CLASS_TOKEN_READ;
|
||||
buffer->cmd_select = SELECT_TOKEN_STD;
|
||||
memset(buffer, 0, sizeof(struct calling_interface_buffer));
|
||||
buffer->input[0] = wsmt->location;
|
||||
buffer->output[0] = 99;
|
||||
dell_smbios_smm_call(buffer);
|
||||
if (buffer->output[0] == 99)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static int __init dell_smbios_smm_init(void)
|
||||
{
|
||||
int ret;
|
||||
/*
|
||||
* Allocate buffer below 4GB for SMI data--only 32-bit physical addr
|
||||
* is passed to SMI handler.
|
||||
*/
|
||||
buffer = (void *)__get_free_page(GFP_KERNEL | GFP_DMA32);
|
||||
if (!buffer)
|
||||
return -ENOMEM;
|
||||
|
||||
dmi_walk(find_cmd_address, NULL);
|
||||
|
||||
if (test_wsmt_enabled()) {
|
||||
pr_debug("Disabling due to WSMT enabled\n");
|
||||
ret = -ENODEV;
|
||||
goto fail_wsmt;
|
||||
}
|
||||
|
||||
platform_device = platform_device_alloc("dell-smbios", 1);
|
||||
if (!platform_device) {
|
||||
ret = -ENOMEM;
|
||||
goto fail_platform_device_alloc;
|
||||
}
|
||||
|
||||
ret = platform_device_add(platform_device);
|
||||
if (ret)
|
||||
goto fail_platform_device_add;
|
||||
|
||||
ret = dell_smbios_register_device(&platform_device->dev,
|
||||
&dell_smbios_smm_call);
|
||||
if (ret)
|
||||
goto fail_register;
|
||||
|
||||
return 0;
|
||||
|
||||
fail_register:
|
||||
platform_device_del(platform_device);
|
||||
|
||||
fail_platform_device_add:
|
||||
platform_device_put(platform_device);
|
||||
|
||||
fail_wsmt:
|
||||
fail_platform_device_alloc:
|
||||
free_page((unsigned long)buffer);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void __exit dell_smbios_smm_exit(void)
|
||||
{
|
||||
if (platform_device) {
|
||||
dell_smbios_unregister_device(&platform_device->dev);
|
||||
platform_device_unregister(platform_device);
|
||||
free_page((unsigned long)buffer);
|
||||
}
|
||||
}
|
||||
|
||||
subsys_initcall(dell_smbios_smm_init);
|
||||
module_exit(dell_smbios_smm_exit);
|
||||
|
||||
MODULE_AUTHOR("Matthew Garrett <mjg@redhat.com>");
|
||||
MODULE_AUTHOR("Gabriele Mazzotta <gabriele.mzt@gmail.com>");
|
||||
MODULE_AUTHOR("Pali Rohár <pali.rohar@gmail.com>");
|
||||
MODULE_AUTHOR("Mario Limonciello <mario.limonciello@dell.com>");
|
||||
MODULE_DESCRIPTION("Dell SMBIOS communications over SMI");
|
||||
MODULE_LICENSE("GPL");
|
||||
272
drivers/platform/x86/dell-smbios-wmi.c
Normal file
272
drivers/platform/x86/dell-smbios-wmi.c
Normal file
|
|
@ -0,0 +1,272 @@
|
|||
/*
|
||||
* WMI methods for use with dell-smbios
|
||||
*
|
||||
* Copyright (c) 2017 Dell Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||
|
||||
#include <linux/dmi.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/wmi.h>
|
||||
#include "dell-smbios.h"
|
||||
#include "dell-wmi-descriptor.h"
|
||||
|
||||
static DEFINE_MUTEX(call_mutex);
|
||||
static DEFINE_MUTEX(list_mutex);
|
||||
static int wmi_supported;
|
||||
|
||||
struct misc_bios_flags_structure {
|
||||
struct dmi_header header;
|
||||
u16 flags0;
|
||||
} __packed;
|
||||
#define FLAG_HAS_ACPI_WMI 0x02
|
||||
|
||||
#define DELL_WMI_SMBIOS_GUID "A80593CE-A997-11DA-B012-B622A1EF5492"
|
||||
|
||||
struct wmi_smbios_priv {
|
||||
struct dell_wmi_smbios_buffer *buf;
|
||||
struct list_head list;
|
||||
struct wmi_device *wdev;
|
||||
struct device *child;
|
||||
u32 req_buf_size;
|
||||
};
|
||||
static LIST_HEAD(wmi_list);
|
||||
|
||||
static inline struct wmi_smbios_priv *get_first_smbios_priv(void)
|
||||
{
|
||||
return list_first_entry_or_null(&wmi_list,
|
||||
struct wmi_smbios_priv,
|
||||
list);
|
||||
}
|
||||
|
||||
static int run_smbios_call(struct wmi_device *wdev)
|
||||
{
|
||||
struct acpi_buffer output = {ACPI_ALLOCATE_BUFFER, NULL};
|
||||
struct wmi_smbios_priv *priv;
|
||||
struct acpi_buffer input;
|
||||
union acpi_object *obj;
|
||||
acpi_status status;
|
||||
|
||||
priv = dev_get_drvdata(&wdev->dev);
|
||||
input.length = priv->req_buf_size - sizeof(u64);
|
||||
input.pointer = &priv->buf->std;
|
||||
|
||||
dev_dbg(&wdev->dev, "evaluating: %u/%u [%x,%x,%x,%x]\n",
|
||||
priv->buf->std.cmd_class, priv->buf->std.cmd_select,
|
||||
priv->buf->std.input[0], priv->buf->std.input[1],
|
||||
priv->buf->std.input[2], priv->buf->std.input[3]);
|
||||
|
||||
status = wmidev_evaluate_method(wdev, 0, 1, &input, &output);
|
||||
if (ACPI_FAILURE(status))
|
||||
return -EIO;
|
||||
obj = (union acpi_object *)output.pointer;
|
||||
if (obj->type != ACPI_TYPE_BUFFER) {
|
||||
dev_dbg(&wdev->dev, "received type: %d\n", obj->type);
|
||||
if (obj->type == ACPI_TYPE_INTEGER)
|
||||
dev_dbg(&wdev->dev, "SMBIOS call failed: %llu\n",
|
||||
obj->integer.value);
|
||||
return -EIO;
|
||||
}
|
||||
memcpy(&priv->buf->std, obj->buffer.pointer, obj->buffer.length);
|
||||
dev_dbg(&wdev->dev, "result: [%08x,%08x,%08x,%08x]\n",
|
||||
priv->buf->std.output[0], priv->buf->std.output[1],
|
||||
priv->buf->std.output[2], priv->buf->std.output[3]);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int dell_smbios_wmi_call(struct calling_interface_buffer *buffer)
|
||||
{
|
||||
struct wmi_smbios_priv *priv;
|
||||
size_t difference;
|
||||
size_t size;
|
||||
int ret;
|
||||
|
||||
mutex_lock(&call_mutex);
|
||||
priv = get_first_smbios_priv();
|
||||
if (!priv) {
|
||||
ret = -ENODEV;
|
||||
goto out_wmi_call;
|
||||
}
|
||||
|
||||
size = sizeof(struct calling_interface_buffer);
|
||||
difference = priv->req_buf_size - sizeof(u64) - size;
|
||||
|
||||
memset(&priv->buf->ext, 0, difference);
|
||||
memcpy(&priv->buf->std, buffer, size);
|
||||
ret = run_smbios_call(priv->wdev);
|
||||
memcpy(buffer, &priv->buf->std, size);
|
||||
out_wmi_call:
|
||||
mutex_unlock(&call_mutex);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static long dell_smbios_wmi_filter(struct wmi_device *wdev, unsigned int cmd,
|
||||
struct wmi_ioctl_buffer *arg)
|
||||
{
|
||||
struct wmi_smbios_priv *priv;
|
||||
int ret = 0;
|
||||
|
||||
switch (cmd) {
|
||||
case DELL_WMI_SMBIOS_CMD:
|
||||
mutex_lock(&call_mutex);
|
||||
priv = dev_get_drvdata(&wdev->dev);
|
||||
if (!priv) {
|
||||
ret = -ENODEV;
|
||||
goto fail_smbios_cmd;
|
||||
}
|
||||
memcpy(priv->buf, arg, priv->req_buf_size);
|
||||
if (dell_smbios_call_filter(&wdev->dev, &priv->buf->std)) {
|
||||
dev_err(&wdev->dev, "Invalid call %d/%d:%8x\n",
|
||||
priv->buf->std.cmd_class,
|
||||
priv->buf->std.cmd_select,
|
||||
priv->buf->std.input[0]);
|
||||
ret = -EFAULT;
|
||||
goto fail_smbios_cmd;
|
||||
}
|
||||
ret = run_smbios_call(priv->wdev);
|
||||
if (ret)
|
||||
goto fail_smbios_cmd;
|
||||
memcpy(arg, priv->buf, priv->req_buf_size);
|
||||
fail_smbios_cmd:
|
||||
mutex_unlock(&call_mutex);
|
||||
break;
|
||||
default:
|
||||
ret = -ENOIOCTLCMD;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int dell_smbios_wmi_probe(struct wmi_device *wdev)
|
||||
{
|
||||
struct wmi_smbios_priv *priv;
|
||||
int count;
|
||||
int ret;
|
||||
|
||||
ret = dell_wmi_get_descriptor_valid();
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
priv = devm_kzalloc(&wdev->dev, sizeof(struct wmi_smbios_priv),
|
||||
GFP_KERNEL);
|
||||
if (!priv)
|
||||
return -ENOMEM;
|
||||
|
||||
/* WMI buffer size will be either 4k or 32k depending on machine */
|
||||
if (!dell_wmi_get_size(&priv->req_buf_size))
|
||||
return -EPROBE_DEFER;
|
||||
|
||||
/* add in the length object we will use internally with ioctl */
|
||||
priv->req_buf_size += sizeof(u64);
|
||||
ret = set_required_buffer_size(wdev, priv->req_buf_size);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
count = get_order(priv->req_buf_size);
|
||||
priv->buf = (void *)__get_free_pages(GFP_KERNEL, count);
|
||||
if (!priv->buf)
|
||||
return -ENOMEM;
|
||||
|
||||
/* ID is used by dell-smbios to set priority of drivers */
|
||||
wdev->dev.id = 1;
|
||||
ret = dell_smbios_register_device(&wdev->dev, &dell_smbios_wmi_call);
|
||||
if (ret)
|
||||
goto fail_register;
|
||||
|
||||
priv->wdev = wdev;
|
||||
dev_set_drvdata(&wdev->dev, priv);
|
||||
mutex_lock(&list_mutex);
|
||||
list_add_tail(&priv->list, &wmi_list);
|
||||
mutex_unlock(&list_mutex);
|
||||
|
||||
return 0;
|
||||
|
||||
fail_register:
|
||||
free_pages((unsigned long)priv->buf, count);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int dell_smbios_wmi_remove(struct wmi_device *wdev)
|
||||
{
|
||||
struct wmi_smbios_priv *priv = dev_get_drvdata(&wdev->dev);
|
||||
int count;
|
||||
|
||||
mutex_lock(&call_mutex);
|
||||
mutex_lock(&list_mutex);
|
||||
list_del(&priv->list);
|
||||
mutex_unlock(&list_mutex);
|
||||
dell_smbios_unregister_device(&wdev->dev);
|
||||
count = get_order(priv->req_buf_size);
|
||||
free_pages((unsigned long)priv->buf, count);
|
||||
mutex_unlock(&call_mutex);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct wmi_device_id dell_smbios_wmi_id_table[] = {
|
||||
{ .guid_string = DELL_WMI_SMBIOS_GUID },
|
||||
{ },
|
||||
};
|
||||
|
||||
static void __init parse_b1_table(const struct dmi_header *dm)
|
||||
{
|
||||
struct misc_bios_flags_structure *flags =
|
||||
container_of(dm, struct misc_bios_flags_structure, header);
|
||||
|
||||
/* 4 bytes header, 8 bytes flags */
|
||||
if (dm->length < 12)
|
||||
return;
|
||||
if (dm->handle != 0xb100)
|
||||
return;
|
||||
if ((flags->flags0 & FLAG_HAS_ACPI_WMI))
|
||||
wmi_supported = 1;
|
||||
}
|
||||
|
||||
static void __init find_b1(const struct dmi_header *dm, void *dummy)
|
||||
{
|
||||
switch (dm->type) {
|
||||
case 0xb1: /* misc bios flags */
|
||||
parse_b1_table(dm);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static struct wmi_driver dell_smbios_wmi_driver = {
|
||||
.driver = {
|
||||
.name = "dell-smbios",
|
||||
},
|
||||
.probe = dell_smbios_wmi_probe,
|
||||
.remove = dell_smbios_wmi_remove,
|
||||
.id_table = dell_smbios_wmi_id_table,
|
||||
.filter_callback = dell_smbios_wmi_filter,
|
||||
};
|
||||
|
||||
static int __init init_dell_smbios_wmi(void)
|
||||
{
|
||||
dmi_walk(find_b1, NULL);
|
||||
|
||||
if (!wmi_supported)
|
||||
return -ENODEV;
|
||||
|
||||
return wmi_driver_register(&dell_smbios_wmi_driver);
|
||||
}
|
||||
|
||||
static void __exit exit_dell_smbios_wmi(void)
|
||||
{
|
||||
wmi_driver_unregister(&dell_smbios_wmi_driver);
|
||||
}
|
||||
|
||||
module_init(init_dell_smbios_wmi);
|
||||
module_exit(exit_dell_smbios_wmi);
|
||||
|
||||
MODULE_ALIAS("wmi:" DELL_WMI_SMBIOS_GUID);
|
||||
MODULE_AUTHOR("Mario Limonciello <mario.limonciello@dell.com>");
|
||||
MODULE_DESCRIPTION("Dell SMBIOS communications over WMI");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
|
@ -12,33 +12,119 @@
|
|||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/capability.h>
|
||||
#include <linux/dmi.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/gfp.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/io.h>
|
||||
#include "../../firmware/dcdbas.h"
|
||||
#include "dell-smbios.h"
|
||||
|
||||
struct calling_interface_structure {
|
||||
struct dmi_header header;
|
||||
u16 cmdIOAddress;
|
||||
u8 cmdIOCode;
|
||||
u32 supportedCmds;
|
||||
struct calling_interface_token tokens[];
|
||||
} __packed;
|
||||
|
||||
static struct calling_interface_buffer *buffer;
|
||||
static DEFINE_MUTEX(buffer_mutex);
|
||||
|
||||
static int da_command_address;
|
||||
static int da_command_code;
|
||||
static u32 da_supported_commands;
|
||||
static int da_num_tokens;
|
||||
static struct platform_device *platform_device;
|
||||
static struct calling_interface_token *da_tokens;
|
||||
static struct device_attribute *token_location_attrs;
|
||||
static struct device_attribute *token_value_attrs;
|
||||
static struct attribute **token_attrs;
|
||||
static DEFINE_MUTEX(smbios_mutex);
|
||||
|
||||
struct smbios_device {
|
||||
struct list_head list;
|
||||
struct device *device;
|
||||
int (*call_fn)(struct calling_interface_buffer *);
|
||||
};
|
||||
|
||||
struct smbios_call {
|
||||
u32 need_capability;
|
||||
int cmd_class;
|
||||
int cmd_select;
|
||||
};
|
||||
|
||||
/* calls that are whitelisted for given capabilities */
|
||||
static struct smbios_call call_whitelist[] = {
|
||||
/* generally tokens are allowed, but may be further filtered or
|
||||
* restricted by token blacklist or whitelist
|
||||
*/
|
||||
{CAP_SYS_ADMIN, CLASS_TOKEN_READ, SELECT_TOKEN_STD},
|
||||
{CAP_SYS_ADMIN, CLASS_TOKEN_READ, SELECT_TOKEN_AC},
|
||||
{CAP_SYS_ADMIN, CLASS_TOKEN_READ, SELECT_TOKEN_BAT},
|
||||
{CAP_SYS_ADMIN, CLASS_TOKEN_WRITE, SELECT_TOKEN_STD},
|
||||
{CAP_SYS_ADMIN, CLASS_TOKEN_WRITE, SELECT_TOKEN_AC},
|
||||
{CAP_SYS_ADMIN, CLASS_TOKEN_WRITE, SELECT_TOKEN_BAT},
|
||||
/* used by userspace: fwupdate */
|
||||
{CAP_SYS_ADMIN, CLASS_ADMIN_PROP, SELECT_ADMIN_PROP},
|
||||
/* used by userspace: fwupd */
|
||||
{CAP_SYS_ADMIN, CLASS_INFO, SELECT_DOCK},
|
||||
{CAP_SYS_ADMIN, CLASS_FLASH_INTERFACE, SELECT_FLASH_INTERFACE},
|
||||
};
|
||||
|
||||
/* calls that are explicitly blacklisted */
|
||||
static struct smbios_call call_blacklist[] = {
|
||||
{0x0000, 01, 07}, /* manufacturing use */
|
||||
{0x0000, 06, 05}, /* manufacturing use */
|
||||
{0x0000, 11, 03}, /* write once */
|
||||
{0x0000, 11, 07}, /* write once */
|
||||
{0x0000, 11, 11}, /* write once */
|
||||
{0x0000, 19, -1}, /* diagnostics */
|
||||
/* handled by kernel: dell-laptop */
|
||||
{0x0000, CLASS_INFO, SELECT_RFKILL},
|
||||
{0x0000, CLASS_KBD_BACKLIGHT, SELECT_KBD_BACKLIGHT},
|
||||
};
|
||||
|
||||
struct token_range {
|
||||
u32 need_capability;
|
||||
u16 min;
|
||||
u16 max;
|
||||
};
|
||||
|
||||
/* tokens that are whitelisted for given capabilities */
|
||||
static struct token_range token_whitelist[] = {
|
||||
/* used by userspace: fwupdate */
|
||||
{CAP_SYS_ADMIN, CAPSULE_EN_TOKEN, CAPSULE_DIS_TOKEN},
|
||||
/* can indicate to userspace that WMI is needed */
|
||||
{0x0000, WSMT_EN_TOKEN, WSMT_DIS_TOKEN}
|
||||
};
|
||||
|
||||
/* tokens that are explicitly blacklisted */
|
||||
static struct token_range token_blacklist[] = {
|
||||
{0x0000, 0x0058, 0x0059}, /* ME use */
|
||||
{0x0000, 0x00CD, 0x00D0}, /* raid shadow copy */
|
||||
{0x0000, 0x013A, 0x01FF}, /* sata shadow copy */
|
||||
{0x0000, 0x0175, 0x0176}, /* write once */
|
||||
{0x0000, 0x0195, 0x0197}, /* diagnostics */
|
||||
{0x0000, 0x01DC, 0x01DD}, /* manufacturing use */
|
||||
{0x0000, 0x027D, 0x0284}, /* diagnostics */
|
||||
{0x0000, 0x02E3, 0x02E3}, /* manufacturing use */
|
||||
{0x0000, 0x02FF, 0x02FF}, /* manufacturing use */
|
||||
{0x0000, 0x0300, 0x0302}, /* manufacturing use */
|
||||
{0x0000, 0x0325, 0x0326}, /* manufacturing use */
|
||||
{0x0000, 0x0332, 0x0335}, /* fan control */
|
||||
{0x0000, 0x0350, 0x0350}, /* manufacturing use */
|
||||
{0x0000, 0x0363, 0x0363}, /* manufacturing use */
|
||||
{0x0000, 0x0368, 0x0368}, /* manufacturing use */
|
||||
{0x0000, 0x03F6, 0x03F7}, /* manufacturing use */
|
||||
{0x0000, 0x049E, 0x049F}, /* manufacturing use */
|
||||
{0x0000, 0x04A0, 0x04A3}, /* disagnostics */
|
||||
{0x0000, 0x04E6, 0x04E7}, /* manufacturing use */
|
||||
{0x0000, 0x4000, 0x7FFF}, /* internal BIOS use */
|
||||
{0x0000, 0x9000, 0x9001}, /* internal BIOS use */
|
||||
{0x0000, 0xA000, 0xBFFF}, /* write only */
|
||||
{0x0000, 0xEFF0, 0xEFFF}, /* internal BIOS use */
|
||||
/* handled by kernel: dell-laptop */
|
||||
{0x0000, BRIGHTNESS_TOKEN, BRIGHTNESS_TOKEN},
|
||||
{0x0000, KBD_LED_OFF_TOKEN, KBD_LED_AUTO_TOKEN},
|
||||
{0x0000, KBD_LED_AC_TOKEN, KBD_LED_AC_TOKEN},
|
||||
{0x0000, KBD_LED_AUTO_25_TOKEN, KBD_LED_AUTO_75_TOKEN},
|
||||
{0x0000, KBD_LED_AUTO_100_TOKEN, KBD_LED_AUTO_100_TOKEN},
|
||||
{0x0000, GLOBAL_MIC_MUTE_ENABLE, GLOBAL_MIC_MUTE_DISABLE},
|
||||
};
|
||||
|
||||
static LIST_HEAD(smbios_device_list);
|
||||
|
||||
int dell_smbios_error(int value)
|
||||
{
|
||||
|
|
@ -55,42 +141,175 @@ int dell_smbios_error(int value)
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(dell_smbios_error);
|
||||
|
||||
struct calling_interface_buffer *dell_smbios_get_buffer(void)
|
||||
int dell_smbios_register_device(struct device *d, void *call_fn)
|
||||
{
|
||||
mutex_lock(&buffer_mutex);
|
||||
dell_smbios_clear_buffer();
|
||||
return buffer;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(dell_smbios_get_buffer);
|
||||
struct smbios_device *priv;
|
||||
|
||||
void dell_smbios_clear_buffer(void)
|
||||
priv = devm_kzalloc(d, sizeof(struct smbios_device), GFP_KERNEL);
|
||||
if (!priv)
|
||||
return -ENOMEM;
|
||||
get_device(d);
|
||||
priv->device = d;
|
||||
priv->call_fn = call_fn;
|
||||
mutex_lock(&smbios_mutex);
|
||||
list_add_tail(&priv->list, &smbios_device_list);
|
||||
mutex_unlock(&smbios_mutex);
|
||||
dev_dbg(d, "Added device: %s\n", d->driver->name);
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(dell_smbios_register_device);
|
||||
|
||||
void dell_smbios_unregister_device(struct device *d)
|
||||
{
|
||||
memset(buffer, 0, sizeof(struct calling_interface_buffer));
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(dell_smbios_clear_buffer);
|
||||
struct smbios_device *priv;
|
||||
|
||||
void dell_smbios_release_buffer(void)
|
||||
mutex_lock(&smbios_mutex);
|
||||
list_for_each_entry(priv, &smbios_device_list, list) {
|
||||
if (priv->device == d) {
|
||||
list_del(&priv->list);
|
||||
put_device(d);
|
||||
break;
|
||||
}
|
||||
}
|
||||
mutex_unlock(&smbios_mutex);
|
||||
dev_dbg(d, "Remove device: %s\n", d->driver->name);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(dell_smbios_unregister_device);
|
||||
|
||||
int dell_smbios_call_filter(struct device *d,
|
||||
struct calling_interface_buffer *buffer)
|
||||
{
|
||||
mutex_unlock(&buffer_mutex);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(dell_smbios_release_buffer);
|
||||
u16 t = 0;
|
||||
int i;
|
||||
|
||||
void dell_smbios_send_request(int class, int select)
|
||||
/* can't make calls over 30 */
|
||||
if (buffer->cmd_class > 30) {
|
||||
dev_dbg(d, "class too big: %u\n", buffer->cmd_class);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* supported calls on the particular system */
|
||||
if (!(da_supported_commands & (1 << buffer->cmd_class))) {
|
||||
dev_dbg(d, "invalid command, supported commands: 0x%8x\n",
|
||||
da_supported_commands);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* match against call blacklist */
|
||||
for (i = 0; i < ARRAY_SIZE(call_blacklist); i++) {
|
||||
if (buffer->cmd_class != call_blacklist[i].cmd_class)
|
||||
continue;
|
||||
if (buffer->cmd_select != call_blacklist[i].cmd_select &&
|
||||
call_blacklist[i].cmd_select != -1)
|
||||
continue;
|
||||
dev_dbg(d, "blacklisted command: %u/%u\n",
|
||||
buffer->cmd_class, buffer->cmd_select);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* if a token call, find token ID */
|
||||
|
||||
if ((buffer->cmd_class == CLASS_TOKEN_READ ||
|
||||
buffer->cmd_class == CLASS_TOKEN_WRITE) &&
|
||||
buffer->cmd_select < 3) {
|
||||
/* find the matching token ID */
|
||||
for (i = 0; i < da_num_tokens; i++) {
|
||||
if (da_tokens[i].location != buffer->input[0])
|
||||
continue;
|
||||
t = da_tokens[i].tokenID;
|
||||
break;
|
||||
}
|
||||
|
||||
/* token call; but token didn't exist */
|
||||
if (!t) {
|
||||
dev_dbg(d, "token at location %04x doesn't exist\n",
|
||||
buffer->input[0]);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* match against token blacklist */
|
||||
for (i = 0; i < ARRAY_SIZE(token_blacklist); i++) {
|
||||
if (!token_blacklist[i].min || !token_blacklist[i].max)
|
||||
continue;
|
||||
if (t >= token_blacklist[i].min &&
|
||||
t <= token_blacklist[i].max)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* match against token whitelist */
|
||||
for (i = 0; i < ARRAY_SIZE(token_whitelist); i++) {
|
||||
if (!token_whitelist[i].min || !token_whitelist[i].max)
|
||||
continue;
|
||||
if (t < token_whitelist[i].min ||
|
||||
t > token_whitelist[i].max)
|
||||
continue;
|
||||
if (!token_whitelist[i].need_capability ||
|
||||
capable(token_whitelist[i].need_capability)) {
|
||||
dev_dbg(d, "whitelisted token: %x\n", t);
|
||||
return 0;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
/* match against call whitelist */
|
||||
for (i = 0; i < ARRAY_SIZE(call_whitelist); i++) {
|
||||
if (buffer->cmd_class != call_whitelist[i].cmd_class)
|
||||
continue;
|
||||
if (buffer->cmd_select != call_whitelist[i].cmd_select)
|
||||
continue;
|
||||
if (!call_whitelist[i].need_capability ||
|
||||
capable(call_whitelist[i].need_capability)) {
|
||||
dev_dbg(d, "whitelisted capable command: %u/%u\n",
|
||||
buffer->cmd_class, buffer->cmd_select);
|
||||
return 0;
|
||||
}
|
||||
dev_dbg(d, "missing capability %d for %u/%u\n",
|
||||
call_whitelist[i].need_capability,
|
||||
buffer->cmd_class, buffer->cmd_select);
|
||||
|
||||
}
|
||||
|
||||
/* not in a whitelist, only allow processes with capabilities */
|
||||
if (capable(CAP_SYS_RAWIO)) {
|
||||
dev_dbg(d, "Allowing %u/%u due to CAP_SYS_RAWIO\n",
|
||||
buffer->cmd_class, buffer->cmd_select);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -EACCES;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(dell_smbios_call_filter);
|
||||
|
||||
int dell_smbios_call(struct calling_interface_buffer *buffer)
|
||||
{
|
||||
struct smi_cmd command;
|
||||
int (*call_fn)(struct calling_interface_buffer *) = NULL;
|
||||
struct device *selected_dev = NULL;
|
||||
struct smbios_device *priv;
|
||||
int ret;
|
||||
|
||||
command.magic = SMI_CMD_MAGIC;
|
||||
command.command_address = da_command_address;
|
||||
command.command_code = da_command_code;
|
||||
command.ebx = virt_to_phys(buffer);
|
||||
command.ecx = 0x42534931;
|
||||
mutex_lock(&smbios_mutex);
|
||||
list_for_each_entry(priv, &smbios_device_list, list) {
|
||||
if (!selected_dev || priv->device->id >= selected_dev->id) {
|
||||
dev_dbg(priv->device, "Trying device ID: %d\n",
|
||||
priv->device->id);
|
||||
call_fn = priv->call_fn;
|
||||
selected_dev = priv->device;
|
||||
}
|
||||
}
|
||||
|
||||
buffer->class = class;
|
||||
buffer->select = select;
|
||||
if (!selected_dev) {
|
||||
ret = -ENODEV;
|
||||
pr_err("No dell-smbios drivers are loaded\n");
|
||||
goto out_smbios_call;
|
||||
}
|
||||
|
||||
dcdbas_smi_request(&command);
|
||||
ret = call_fn(buffer);
|
||||
|
||||
out_smbios_call:
|
||||
mutex_unlock(&smbios_mutex);
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(dell_smbios_send_request);
|
||||
EXPORT_SYMBOL_GPL(dell_smbios_call);
|
||||
|
||||
struct calling_interface_token *dell_smbios_find_token(int tokenid)
|
||||
{
|
||||
|
|
@ -139,8 +358,7 @@ static void __init parse_da_table(const struct dmi_header *dm)
|
|||
if (dm->length < 17)
|
||||
return;
|
||||
|
||||
da_command_address = table->cmdIOAddress;
|
||||
da_command_code = table->cmdIOCode;
|
||||
da_supported_commands = table->supportedCmds;
|
||||
|
||||
new_da_tokens = krealloc(da_tokens, (da_num_tokens + tokens) *
|
||||
sizeof(struct calling_interface_token),
|
||||
|
|
@ -156,6 +374,27 @@ static void __init parse_da_table(const struct dmi_header *dm)
|
|||
da_num_tokens += tokens;
|
||||
}
|
||||
|
||||
static void zero_duplicates(struct device *dev)
|
||||
{
|
||||
int i, j;
|
||||
|
||||
for (i = 0; i < da_num_tokens; i++) {
|
||||
if (da_tokens[i].tokenID == 0)
|
||||
continue;
|
||||
for (j = i+1; j < da_num_tokens; j++) {
|
||||
if (da_tokens[j].tokenID == 0)
|
||||
continue;
|
||||
if (da_tokens[i].tokenID == da_tokens[j].tokenID) {
|
||||
dev_dbg(dev, "Zeroing dup token ID %x(%x/%x)\n",
|
||||
da_tokens[j].tokenID,
|
||||
da_tokens[j].location,
|
||||
da_tokens[j].value);
|
||||
da_tokens[j].tokenID = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void __init find_tokens(const struct dmi_header *dm, void *dummy)
|
||||
{
|
||||
switch (dm->type) {
|
||||
|
|
@ -169,10 +408,160 @@ static void __init find_tokens(const struct dmi_header *dm, void *dummy)
|
|||
}
|
||||
}
|
||||
|
||||
static int match_attribute(struct device *dev,
|
||||
struct device_attribute *attr)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < da_num_tokens * 2; i++) {
|
||||
if (!token_attrs[i])
|
||||
continue;
|
||||
if (strcmp(token_attrs[i]->name, attr->attr.name) == 0)
|
||||
return i/2;
|
||||
}
|
||||
dev_dbg(dev, "couldn't match: %s\n", attr->attr.name);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static ssize_t location_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (!capable(CAP_SYS_ADMIN))
|
||||
return -EPERM;
|
||||
|
||||
i = match_attribute(dev, attr);
|
||||
if (i > 0)
|
||||
return scnprintf(buf, PAGE_SIZE, "%08x", da_tokens[i].location);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t value_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (!capable(CAP_SYS_ADMIN))
|
||||
return -EPERM;
|
||||
|
||||
i = match_attribute(dev, attr);
|
||||
if (i > 0)
|
||||
return scnprintf(buf, PAGE_SIZE, "%08x", da_tokens[i].value);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct attribute_group smbios_attribute_group = {
|
||||
.name = "tokens"
|
||||
};
|
||||
|
||||
static struct platform_driver platform_driver = {
|
||||
.driver = {
|
||||
.name = "dell-smbios",
|
||||
},
|
||||
};
|
||||
|
||||
static int build_tokens_sysfs(struct platform_device *dev)
|
||||
{
|
||||
char *location_name;
|
||||
char *value_name;
|
||||
size_t size;
|
||||
int ret;
|
||||
int i, j;
|
||||
|
||||
/* (number of tokens + 1 for null terminated */
|
||||
size = sizeof(struct device_attribute) * (da_num_tokens + 1);
|
||||
token_location_attrs = kzalloc(size, GFP_KERNEL);
|
||||
if (!token_location_attrs)
|
||||
return -ENOMEM;
|
||||
token_value_attrs = kzalloc(size, GFP_KERNEL);
|
||||
if (!token_value_attrs)
|
||||
goto out_allocate_value;
|
||||
|
||||
/* need to store both location and value + terminator*/
|
||||
size = sizeof(struct attribute *) * ((2 * da_num_tokens) + 1);
|
||||
token_attrs = kzalloc(size, GFP_KERNEL);
|
||||
if (!token_attrs)
|
||||
goto out_allocate_attrs;
|
||||
|
||||
for (i = 0, j = 0; i < da_num_tokens; i++) {
|
||||
/* skip empty */
|
||||
if (da_tokens[i].tokenID == 0)
|
||||
continue;
|
||||
/* add location */
|
||||
location_name = kasprintf(GFP_KERNEL, "%04x_location",
|
||||
da_tokens[i].tokenID);
|
||||
if (location_name == NULL)
|
||||
goto out_unwind_strings;
|
||||
sysfs_attr_init(&token_location_attrs[i].attr);
|
||||
token_location_attrs[i].attr.name = location_name;
|
||||
token_location_attrs[i].attr.mode = 0444;
|
||||
token_location_attrs[i].show = location_show;
|
||||
token_attrs[j++] = &token_location_attrs[i].attr;
|
||||
|
||||
/* add value */
|
||||
value_name = kasprintf(GFP_KERNEL, "%04x_value",
|
||||
da_tokens[i].tokenID);
|
||||
if (value_name == NULL)
|
||||
goto loop_fail_create_value;
|
||||
sysfs_attr_init(&token_value_attrs[i].attr);
|
||||
token_value_attrs[i].attr.name = value_name;
|
||||
token_value_attrs[i].attr.mode = 0444;
|
||||
token_value_attrs[i].show = value_show;
|
||||
token_attrs[j++] = &token_value_attrs[i].attr;
|
||||
continue;
|
||||
|
||||
loop_fail_create_value:
|
||||
kfree(value_name);
|
||||
goto out_unwind_strings;
|
||||
}
|
||||
smbios_attribute_group.attrs = token_attrs;
|
||||
|
||||
ret = sysfs_create_group(&dev->dev.kobj, &smbios_attribute_group);
|
||||
if (ret)
|
||||
goto out_unwind_strings;
|
||||
return 0;
|
||||
|
||||
out_unwind_strings:
|
||||
for (i = i-1; i > 0; i--) {
|
||||
kfree(token_location_attrs[i].attr.name);
|
||||
kfree(token_value_attrs[i].attr.name);
|
||||
}
|
||||
kfree(token_attrs);
|
||||
out_allocate_attrs:
|
||||
kfree(token_value_attrs);
|
||||
out_allocate_value:
|
||||
kfree(token_location_attrs);
|
||||
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
static void free_group(struct platform_device *pdev)
|
||||
{
|
||||
int i;
|
||||
|
||||
sysfs_remove_group(&pdev->dev.kobj,
|
||||
&smbios_attribute_group);
|
||||
for (i = 0; i < da_num_tokens; i++) {
|
||||
kfree(token_location_attrs[i].attr.name);
|
||||
kfree(token_value_attrs[i].attr.name);
|
||||
}
|
||||
kfree(token_attrs);
|
||||
kfree(token_value_attrs);
|
||||
kfree(token_location_attrs);
|
||||
}
|
||||
|
||||
static int __init dell_smbios_init(void)
|
||||
{
|
||||
const struct dmi_device *valid;
|
||||
int ret;
|
||||
|
||||
valid = dmi_find_device(DMI_DEV_TYPE_OEM_STRING, "Dell System", NULL);
|
||||
if (!valid) {
|
||||
pr_err("Unable to run on non-Dell system\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
dmi_walk(find_tokens, NULL);
|
||||
|
||||
if (!da_tokens) {
|
||||
|
|
@ -180,27 +569,52 @@ static int __init dell_smbios_init(void)
|
|||
return -ENODEV;
|
||||
}
|
||||
|
||||
/*
|
||||
* Allocate buffer below 4GB for SMI data--only 32-bit physical addr
|
||||
* is passed to SMI handler.
|
||||
*/
|
||||
buffer = (void *)__get_free_page(GFP_KERNEL | GFP_DMA32);
|
||||
if (!buffer) {
|
||||
ret = platform_driver_register(&platform_driver);
|
||||
if (ret)
|
||||
goto fail_platform_driver;
|
||||
|
||||
platform_device = platform_device_alloc("dell-smbios", 0);
|
||||
if (!platform_device) {
|
||||
ret = -ENOMEM;
|
||||
goto fail_buffer;
|
||||
goto fail_platform_device_alloc;
|
||||
}
|
||||
ret = platform_device_add(platform_device);
|
||||
if (ret)
|
||||
goto fail_platform_device_add;
|
||||
|
||||
/* duplicate tokens will cause problems building sysfs files */
|
||||
zero_duplicates(&platform_device->dev);
|
||||
|
||||
ret = build_tokens_sysfs(platform_device);
|
||||
if (ret)
|
||||
goto fail_create_group;
|
||||
|
||||
return 0;
|
||||
|
||||
fail_buffer:
|
||||
fail_create_group:
|
||||
platform_device_del(platform_device);
|
||||
|
||||
fail_platform_device_add:
|
||||
platform_device_put(platform_device);
|
||||
|
||||
fail_platform_device_alloc:
|
||||
platform_driver_unregister(&platform_driver);
|
||||
|
||||
fail_platform_driver:
|
||||
kfree(da_tokens);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void __exit dell_smbios_exit(void)
|
||||
{
|
||||
mutex_lock(&smbios_mutex);
|
||||
if (platform_device) {
|
||||
free_group(platform_device);
|
||||
platform_device_unregister(platform_device);
|
||||
platform_driver_unregister(&platform_driver);
|
||||
}
|
||||
kfree(da_tokens);
|
||||
free_page((unsigned long)buffer);
|
||||
mutex_unlock(&smbios_mutex);
|
||||
}
|
||||
|
||||
subsys_initcall(dell_smbios_init);
|
||||
|
|
|
|||
|
|
@ -16,18 +16,30 @@
|
|||
#ifndef _DELL_SMBIOS_H_
|
||||
#define _DELL_SMBIOS_H_
|
||||
|
||||
#include <linux/device.h>
|
||||
#include <uapi/linux/wmi.h>
|
||||
|
||||
/* Classes and selects used only in kernel drivers */
|
||||
#define CLASS_KBD_BACKLIGHT 4
|
||||
#define SELECT_KBD_BACKLIGHT 11
|
||||
|
||||
/* Tokens used in kernel drivers, any of these
|
||||
* should be filtered from userspace access
|
||||
*/
|
||||
#define BRIGHTNESS_TOKEN 0x007d
|
||||
#define KBD_LED_AC_TOKEN 0x0451
|
||||
#define KBD_LED_OFF_TOKEN 0x01E1
|
||||
#define KBD_LED_ON_TOKEN 0x01E2
|
||||
#define KBD_LED_AUTO_TOKEN 0x01E3
|
||||
#define KBD_LED_AUTO_25_TOKEN 0x02EA
|
||||
#define KBD_LED_AUTO_50_TOKEN 0x02EB
|
||||
#define KBD_LED_AUTO_75_TOKEN 0x02EC
|
||||
#define KBD_LED_AUTO_100_TOKEN 0x02F6
|
||||
#define GLOBAL_MIC_MUTE_ENABLE 0x0364
|
||||
#define GLOBAL_MIC_MUTE_DISABLE 0x0365
|
||||
|
||||
struct notifier_block;
|
||||
|
||||
/* This structure will be modified by the firmware when we enter
|
||||
* system management mode, hence the volatiles */
|
||||
|
||||
struct calling_interface_buffer {
|
||||
u16 class;
|
||||
u16 select;
|
||||
volatile u32 input[4];
|
||||
volatile u32 output[4];
|
||||
} __packed;
|
||||
|
||||
struct calling_interface_token {
|
||||
u16 tokenID;
|
||||
u16 location;
|
||||
|
|
@ -37,12 +49,21 @@ struct calling_interface_token {
|
|||
};
|
||||
};
|
||||
|
||||
int dell_smbios_error(int value);
|
||||
struct calling_interface_structure {
|
||||
struct dmi_header header;
|
||||
u16 cmdIOAddress;
|
||||
u8 cmdIOCode;
|
||||
u32 supportedCmds;
|
||||
struct calling_interface_token tokens[];
|
||||
} __packed;
|
||||
|
||||
struct calling_interface_buffer *dell_smbios_get_buffer(void);
|
||||
void dell_smbios_clear_buffer(void);
|
||||
void dell_smbios_release_buffer(void);
|
||||
void dell_smbios_send_request(int class, int select);
|
||||
int dell_smbios_register_device(struct device *d, void *call_fn);
|
||||
void dell_smbios_unregister_device(struct device *d);
|
||||
|
||||
int dell_smbios_error(int value);
|
||||
int dell_smbios_call_filter(struct device *d,
|
||||
struct calling_interface_buffer *buffer);
|
||||
int dell_smbios_call(struct calling_interface_buffer *buffer);
|
||||
|
||||
struct calling_interface_token *dell_smbios_find_token(int tokenid);
|
||||
|
||||
|
|
|
|||
|
|
@ -90,7 +90,7 @@ static ssize_t smo8800_misc_read(struct file *file, char __user *buf,
|
|||
struct smo8800_device, miscdev);
|
||||
|
||||
u32 data = 0;
|
||||
unsigned char byte_data = 0;
|
||||
unsigned char byte_data;
|
||||
ssize_t retval = 1;
|
||||
|
||||
if (count < 1)
|
||||
|
|
@ -103,7 +103,6 @@ static ssize_t smo8800_misc_read(struct file *file, char __user *buf,
|
|||
if (retval)
|
||||
return retval;
|
||||
|
||||
byte_data = 1;
|
||||
retval = 1;
|
||||
|
||||
if (data < 255)
|
||||
|
|
|
|||
191
drivers/platform/x86/dell-wmi-descriptor.c
Normal file
191
drivers/platform/x86/dell-wmi-descriptor.c
Normal file
|
|
@ -0,0 +1,191 @@
|
|||
/*
|
||||
* Dell WMI descriptor driver
|
||||
*
|
||||
* Copyright (C) 2017 Dell Inc. All Rights Reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 as published
|
||||
* by the Free Software Foundation.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/wmi.h>
|
||||
#include "dell-wmi-descriptor.h"
|
||||
|
||||
#define DELL_WMI_DESCRIPTOR_GUID "8D9DDCBC-A997-11DA-B012-B622A1EF5492"
|
||||
|
||||
struct descriptor_priv {
|
||||
struct list_head list;
|
||||
u32 interface_version;
|
||||
u32 size;
|
||||
};
|
||||
static int descriptor_valid = -EPROBE_DEFER;
|
||||
static LIST_HEAD(wmi_list);
|
||||
static DEFINE_MUTEX(list_mutex);
|
||||
|
||||
int dell_wmi_get_descriptor_valid(void)
|
||||
{
|
||||
if (!wmi_has_guid(DELL_WMI_DESCRIPTOR_GUID))
|
||||
return -ENODEV;
|
||||
|
||||
return descriptor_valid;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(dell_wmi_get_descriptor_valid);
|
||||
|
||||
bool dell_wmi_get_interface_version(u32 *version)
|
||||
{
|
||||
struct descriptor_priv *priv;
|
||||
bool ret = false;
|
||||
|
||||
mutex_lock(&list_mutex);
|
||||
priv = list_first_entry_or_null(&wmi_list,
|
||||
struct descriptor_priv,
|
||||
list);
|
||||
if (priv) {
|
||||
*version = priv->interface_version;
|
||||
ret = true;
|
||||
}
|
||||
mutex_unlock(&list_mutex);
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(dell_wmi_get_interface_version);
|
||||
|
||||
bool dell_wmi_get_size(u32 *size)
|
||||
{
|
||||
struct descriptor_priv *priv;
|
||||
bool ret = false;
|
||||
|
||||
mutex_lock(&list_mutex);
|
||||
priv = list_first_entry_or_null(&wmi_list,
|
||||
struct descriptor_priv,
|
||||
list);
|
||||
if (priv) {
|
||||
*size = priv->size;
|
||||
ret = true;
|
||||
}
|
||||
mutex_unlock(&list_mutex);
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(dell_wmi_get_size);
|
||||
|
||||
/*
|
||||
* Descriptor buffer is 128 byte long and contains:
|
||||
*
|
||||
* Name Offset Length Value
|
||||
* Vendor Signature 0 4 "DELL"
|
||||
* Object Signature 4 4 " WMI"
|
||||
* WMI Interface Version 8 4 <version>
|
||||
* WMI buffer length 12 4 <length>
|
||||
*/
|
||||
static int dell_wmi_descriptor_probe(struct wmi_device *wdev)
|
||||
{
|
||||
union acpi_object *obj = NULL;
|
||||
struct descriptor_priv *priv;
|
||||
u32 *buffer;
|
||||
int ret;
|
||||
|
||||
obj = wmidev_block_query(wdev, 0);
|
||||
if (!obj) {
|
||||
dev_err(&wdev->dev, "failed to read Dell WMI descriptor\n");
|
||||
ret = -EIO;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (obj->type != ACPI_TYPE_BUFFER) {
|
||||
dev_err(&wdev->dev, "Dell descriptor has wrong type\n");
|
||||
ret = -EINVAL;
|
||||
descriptor_valid = ret;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Although it's not technically a failure, this would lead to
|
||||
* unexpected behavior
|
||||
*/
|
||||
if (obj->buffer.length != 128) {
|
||||
dev_err(&wdev->dev,
|
||||
"Dell descriptor buffer has unexpected length (%d)\n",
|
||||
obj->buffer.length);
|
||||
ret = -EINVAL;
|
||||
descriptor_valid = ret;
|
||||
goto out;
|
||||
}
|
||||
|
||||
buffer = (u32 *)obj->buffer.pointer;
|
||||
|
||||
if (strncmp(obj->string.pointer, "DELL WMI", 8) != 0) {
|
||||
dev_err(&wdev->dev, "Dell descriptor buffer has invalid signature (%8ph)\n",
|
||||
buffer);
|
||||
ret = -EINVAL;
|
||||
descriptor_valid = ret;
|
||||
goto out;
|
||||
}
|
||||
descriptor_valid = 0;
|
||||
|
||||
if (buffer[2] != 0 && buffer[2] != 1)
|
||||
dev_warn(&wdev->dev, "Dell descriptor buffer has unknown version (%lu)\n",
|
||||
(unsigned long) buffer[2]);
|
||||
|
||||
priv = devm_kzalloc(&wdev->dev, sizeof(struct descriptor_priv),
|
||||
GFP_KERNEL);
|
||||
|
||||
if (!priv) {
|
||||
ret = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
priv->interface_version = buffer[2];
|
||||
priv->size = buffer[3];
|
||||
ret = 0;
|
||||
dev_set_drvdata(&wdev->dev, priv);
|
||||
mutex_lock(&list_mutex);
|
||||
list_add_tail(&priv->list, &wmi_list);
|
||||
mutex_unlock(&list_mutex);
|
||||
|
||||
dev_dbg(&wdev->dev, "Detected Dell WMI interface version %lu and buffer size %lu\n",
|
||||
(unsigned long) priv->interface_version,
|
||||
(unsigned long) priv->size);
|
||||
|
||||
out:
|
||||
kfree(obj);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int dell_wmi_descriptor_remove(struct wmi_device *wdev)
|
||||
{
|
||||
struct descriptor_priv *priv = dev_get_drvdata(&wdev->dev);
|
||||
|
||||
mutex_lock(&list_mutex);
|
||||
list_del(&priv->list);
|
||||
mutex_unlock(&list_mutex);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct wmi_device_id dell_wmi_descriptor_id_table[] = {
|
||||
{ .guid_string = DELL_WMI_DESCRIPTOR_GUID },
|
||||
{ },
|
||||
};
|
||||
|
||||
static struct wmi_driver dell_wmi_descriptor_driver = {
|
||||
.driver = {
|
||||
.name = "dell-wmi-descriptor",
|
||||
},
|
||||
.probe = dell_wmi_descriptor_probe,
|
||||
.remove = dell_wmi_descriptor_remove,
|
||||
.id_table = dell_wmi_descriptor_id_table,
|
||||
};
|
||||
|
||||
module_wmi_driver(dell_wmi_descriptor_driver);
|
||||
|
||||
MODULE_ALIAS("wmi:" DELL_WMI_DESCRIPTOR_GUID);
|
||||
MODULE_AUTHOR("Mario Limonciello <mario.limonciello@dell.com>");
|
||||
MODULE_DESCRIPTION("Dell WMI descriptor driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
27
drivers/platform/x86/dell-wmi-descriptor.h
Normal file
27
drivers/platform/x86/dell-wmi-descriptor.h
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
/*
|
||||
* Dell WMI descriptor driver
|
||||
*
|
||||
* Copyright (c) 2017 Dell Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#ifndef _DELL_WMI_DESCRIPTOR_H_
|
||||
#define _DELL_WMI_DESCRIPTOR_H_
|
||||
|
||||
#include <linux/wmi.h>
|
||||
|
||||
/* possible return values:
|
||||
* -ENODEV: Descriptor GUID missing from WMI bus
|
||||
* -EPROBE_DEFER: probing for dell-wmi-descriptor not yet run
|
||||
* 0: valid descriptor, successfully probed
|
||||
* < 0: invalid descriptor, don't probe dependent devices
|
||||
*/
|
||||
int dell_wmi_get_descriptor_valid(void);
|
||||
|
||||
bool dell_wmi_get_interface_version(u32 *version);
|
||||
bool dell_wmi_get_size(u32 *size);
|
||||
|
||||
#endif
|
||||
|
|
@ -39,6 +39,7 @@
|
|||
#include <linux/wmi.h>
|
||||
#include <acpi/video.h>
|
||||
#include "dell-smbios.h"
|
||||
#include "dell-wmi-descriptor.h"
|
||||
|
||||
MODULE_AUTHOR("Matthew Garrett <mjg@redhat.com>");
|
||||
MODULE_AUTHOR("Pali Rohár <pali.rohar@gmail.com>");
|
||||
|
|
@ -46,12 +47,10 @@ MODULE_DESCRIPTION("Dell laptop WMI hotkeys driver");
|
|||
MODULE_LICENSE("GPL");
|
||||
|
||||
#define DELL_EVENT_GUID "9DBB5994-A997-11DA-B012-B622A1EF5492"
|
||||
#define DELL_DESCRIPTOR_GUID "8D9DDCBC-A997-11DA-B012-B622A1EF5492"
|
||||
|
||||
static bool wmi_requires_smbios_request;
|
||||
|
||||
MODULE_ALIAS("wmi:"DELL_EVENT_GUID);
|
||||
MODULE_ALIAS("wmi:"DELL_DESCRIPTOR_GUID);
|
||||
|
||||
struct dell_wmi_priv {
|
||||
struct input_dev *input_dev;
|
||||
|
|
@ -618,78 +617,6 @@ static void dell_wmi_input_destroy(struct wmi_device *wdev)
|
|||
input_unregister_device(priv->input_dev);
|
||||
}
|
||||
|
||||
/*
|
||||
* Descriptor buffer is 128 byte long and contains:
|
||||
*
|
||||
* Name Offset Length Value
|
||||
* Vendor Signature 0 4 "DELL"
|
||||
* Object Signature 4 4 " WMI"
|
||||
* WMI Interface Version 8 4 <version>
|
||||
* WMI buffer length 12 4 4096
|
||||
*/
|
||||
static int dell_wmi_check_descriptor_buffer(struct wmi_device *wdev)
|
||||
{
|
||||
struct dell_wmi_priv *priv = dev_get_drvdata(&wdev->dev);
|
||||
union acpi_object *obj = NULL;
|
||||
struct wmi_device *desc_dev;
|
||||
u32 *buffer;
|
||||
int ret;
|
||||
|
||||
desc_dev = wmidev_get_other_guid(wdev, DELL_DESCRIPTOR_GUID);
|
||||
if (!desc_dev) {
|
||||
dev_err(&wdev->dev, "Dell WMI descriptor does not exist\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
obj = wmidev_block_query(desc_dev, 0);
|
||||
if (!obj) {
|
||||
dev_err(&wdev->dev, "failed to read Dell WMI descriptor\n");
|
||||
ret = -EIO;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (obj->type != ACPI_TYPE_BUFFER) {
|
||||
dev_err(&wdev->dev, "Dell descriptor has wrong type\n");
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (obj->buffer.length != 128) {
|
||||
dev_err(&wdev->dev,
|
||||
"Dell descriptor buffer has invalid length (%d)\n",
|
||||
obj->buffer.length);
|
||||
if (obj->buffer.length < 16) {
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
buffer = (u32 *)obj->buffer.pointer;
|
||||
|
||||
if (buffer[0] != 0x4C4C4544 && buffer[1] != 0x494D5720)
|
||||
dev_warn(&wdev->dev, "Dell descriptor buffer has invalid signature (%*ph)\n",
|
||||
8, buffer);
|
||||
|
||||
if (buffer[2] != 0 && buffer[2] != 1)
|
||||
dev_warn(&wdev->dev, "Dell descriptor buffer has unknown version (%d)\n",
|
||||
buffer[2]);
|
||||
|
||||
if (buffer[3] != 4096)
|
||||
dev_warn(&wdev->dev, "Dell descriptor buffer has invalid buffer length (%d)\n",
|
||||
buffer[3]);
|
||||
|
||||
priv->interface_version = buffer[2];
|
||||
ret = 0;
|
||||
|
||||
dev_info(&wdev->dev, "Detected Dell WMI interface version %u\n",
|
||||
priv->interface_version);
|
||||
|
||||
out:
|
||||
kfree(obj);
|
||||
put_device(&desc_dev->dev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* According to Dell SMBIOS documentation:
|
||||
*
|
||||
|
|
@ -711,13 +638,16 @@ static int dell_wmi_events_set_enabled(bool enable)
|
|||
struct calling_interface_buffer *buffer;
|
||||
int ret;
|
||||
|
||||
buffer = dell_smbios_get_buffer();
|
||||
buffer = kzalloc(sizeof(struct calling_interface_buffer), GFP_KERNEL);
|
||||
buffer->cmd_class = CLASS_INFO;
|
||||
buffer->cmd_select = SELECT_APP_REGISTRATION;
|
||||
buffer->input[0] = 0x10000;
|
||||
buffer->input[1] = 0x51534554;
|
||||
buffer->input[3] = enable;
|
||||
dell_smbios_send_request(17, 3);
|
||||
ret = buffer->output[0];
|
||||
dell_smbios_release_buffer();
|
||||
ret = dell_smbios_call(buffer);
|
||||
if (ret == 0)
|
||||
ret = buffer->output[0];
|
||||
kfree(buffer);
|
||||
|
||||
return dell_smbios_error(ret);
|
||||
}
|
||||
|
|
@ -725,7 +655,11 @@ static int dell_wmi_events_set_enabled(bool enable)
|
|||
static int dell_wmi_probe(struct wmi_device *wdev)
|
||||
{
|
||||
struct dell_wmi_priv *priv;
|
||||
int err;
|
||||
int ret;
|
||||
|
||||
ret = dell_wmi_get_descriptor_valid();
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
priv = devm_kzalloc(
|
||||
&wdev->dev, sizeof(struct dell_wmi_priv), GFP_KERNEL);
|
||||
|
|
@ -733,9 +667,8 @@ static int dell_wmi_probe(struct wmi_device *wdev)
|
|||
return -ENOMEM;
|
||||
dev_set_drvdata(&wdev->dev, priv);
|
||||
|
||||
err = dell_wmi_check_descriptor_buffer(wdev);
|
||||
if (err)
|
||||
return err;
|
||||
if (!dell_wmi_get_interface_version(&priv->interface_version))
|
||||
return -EPROBE_DEFER;
|
||||
|
||||
return dell_wmi_input_setup(wdev);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -691,6 +691,7 @@ static enum led_brightness eco_led_get(struct led_classdev *cdev)
|
|||
|
||||
static int acpi_fujitsu_laptop_leds_register(struct acpi_device *device)
|
||||
{
|
||||
struct fujitsu_laptop *priv = acpi_driver_data(device);
|
||||
struct led_classdev *led;
|
||||
int result;
|
||||
|
||||
|
|
@ -724,12 +725,15 @@ static int acpi_fujitsu_laptop_leds_register(struct acpi_device *device)
|
|||
}
|
||||
|
||||
/*
|
||||
* BTNI bit 24 seems to indicate the presence of a radio toggle
|
||||
* button in place of a slide switch, and all such machines appear
|
||||
* to also have an RF LED. Therefore use bit 24 as an indicator
|
||||
* that an RF LED is present.
|
||||
* Some Fujitsu laptops have a radio toggle button in place of a slide
|
||||
* switch and all such machines appear to also have an RF LED. Based on
|
||||
* comparing DSDT tables of four Fujitsu Lifebook models (E744, E751,
|
||||
* S7110, S8420; the first one has a radio toggle button, the other
|
||||
* three have slide switches), bit 17 of flags_supported (the value
|
||||
* returned by method S000 of ACPI device FUJ02E3) seems to indicate
|
||||
* whether given model has a radio toggle button.
|
||||
*/
|
||||
if (call_fext_func(device, FUNC_BUTTONS, 0x0, 0x0, 0x0) & BIT(24)) {
|
||||
if (priv->flags_supported & BIT(17)) {
|
||||
led = devm_kzalloc(&device->dev, sizeof(*led), GFP_KERNEL);
|
||||
if (!led)
|
||||
return -ENOMEM;
|
||||
|
|
|
|||
|
|
@ -297,7 +297,7 @@ static int hp_wmi_hw_state(int mask)
|
|||
if (state < 0)
|
||||
return state;
|
||||
|
||||
return state & 0x1;
|
||||
return !!(state & mask);
|
||||
}
|
||||
|
||||
static int __init hp_wmi_bios_2008_later(void)
|
||||
|
|
|
|||
|
|
@ -240,6 +240,7 @@ static const struct dmi_system_id lis3lv02d_dmi_ids[] = {
|
|||
AXIS_DMI_MATCH("HDX18", "HP HDX 18", x_inverted),
|
||||
AXIS_DMI_MATCH("HPB432x", "HP ProBook 432", xy_rotated_left),
|
||||
AXIS_DMI_MATCH("HPB440G3", "HP ProBook 440 G3", x_inverted_usd),
|
||||
AXIS_DMI_MATCH("HPB440G4", "HP ProBook 440 G4", x_inverted),
|
||||
AXIS_DMI_MATCH("HPB442x", "HP ProBook 442", xy_rotated_left),
|
||||
AXIS_DMI_MATCH("HPB452x", "HP ProBook 452", y_inverted),
|
||||
AXIS_DMI_MATCH("HPB522x", "HP ProBook 522", xy_swap),
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue