Commit 07c455ee authored by Linus Torvalds's avatar Linus Torvalds

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
  ...
parents 1deab8ce aaa40965
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.
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.
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.
......@@ -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
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 module provides common functions for kernel modules using
Dell SMBIOS.
This provides an implementation for the Dell SMBIOS calling interface
communicated over SMI/SMM.
If you have a Dell laptop, say Y or M here.
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:
......
This diff is collapsed.
/*
* 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");
/*
* 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");
This diff is collapsed.
......@@ -16,17 +16,29 @@
#ifndef _DELL_SMBIOS_H_
#define _DELL_SMBIOS_H_
struct notifier_block;
#include <linux/device.h>
#include <uapi/linux/wmi.h>
/* This structure will be modified by the firmware when we enter
* system management mode, hence the volatiles */
/* Classes and selects used only in kernel drivers */
#define CLASS_KBD_BACKLIGHT 4
#define SELECT_KBD_BACKLIGHT 11
struct calling_interface_buffer {
u16 class;
u16 select;
volatile u32 input[4];
volatile u32 output[4];
} __packed;
/* 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;
struct calling_interface_token {
u16 tokenID;
......@@ -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)
......
/*
* 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");
/*
* 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),
......
......@@ -1166,6 +1166,13 @@ static const struct dmi_system_id no_hw_rfkill_list[] = {
DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo YOGA 910-13IKB"),
},
},
{
.ident = "Lenovo YOGA 920-13IKB",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo YOGA 920-13IKB"),
},
},
{}
};
......
......@@ -226,6 +226,24 @@ static void notify_handler(acpi_handle handle, u32 event, void *context)
return;
}
/*
* Needed for suspend to work on some platforms that don't expose
* the 5-button array, but still send notifies with power button
* event code to this device object on power button actions.
*
* Report the power button press; catch and ignore the button release.
*/
if (!priv->array) {
if (event == 0xce) {
input_report_key(priv->input_dev, KEY_POWER, 1);
input_sync(priv->input_dev);
return;
}
if (event == 0xcf)
return;
}
/* 0xC0 is for HID events, other values are for 5 button array */
if (event != 0xc0) {
if (!priv->array ||
......
/*
* WMI Thunderbolt 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/device.h>
#include <linux/fs.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/string.h>
#include <linux/sysfs.h>
#include <linux/types.h>
#include <linux/wmi.h>
#define INTEL_WMI_THUNDERBOLT_GUID "86CCFD48-205E-4A77-9C48-2021CBEDE341"
static ssize_t force_power_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct acpi_buffer input;
acpi_status status;
u8 mode;
input.length = sizeof(u8);
input.pointer = &mode;
mode = hex_to_bin(buf[0]);
if (mode == 0 || mode == 1) {
status = wmi_evaluate_method(INTEL_WMI_THUNDERBOLT_GUID, 0, 1,
&input, NULL);
if (ACPI_FAILURE(status))
return -ENODEV;
} else {
return -EINVAL;
}
return count;
}
static DEVICE_ATTR_WO(force_power);
static struct attribute *tbt_attrs[] = {
&dev_attr_force_power.attr,
NULL
};
static const struct attribute_group tbt_attribute_group = {
.attrs = tbt_attrs,
};
static int intel_wmi_thunderbolt_probe(struct wmi_device *wdev)
{
int ret;
ret = sysfs_create_group(&wdev->dev.kobj, &tbt_attribute_group);
kobject_uevent(&wdev->dev.kobj, KOBJ_CHANGE);
return ret;
}
static int intel_wmi_thunderbolt_remove(struct wmi_device *wdev)
{
sysfs_remove_group(&wdev->dev.kobj, &tbt_attribute_group);
kobject_uevent(&wdev->dev.kobj, KOBJ_CHANGE);
return 0;
}
static const struct wmi_device_id intel_wmi_thunderbolt_id_table[] = {
{ .guid_string = INTEL_WMI_THUNDERBOLT_GUID },
{ },
};
static struct wmi_driver intel_wmi_thunderbolt_driver = {
.driver = {
.name = "intel-wmi-thunderbolt",
},
.probe = intel_wmi_thunderbolt_probe,
.remove = intel_wmi_thunderbolt_remove,
.id_table = intel_wmi_thunderbolt_id_table,
};
module_wmi_driver(intel_wmi_thunderbolt_driver);
MODULE_ALIAS("wmi:" INTEL_WMI_THUNDERBOLT_GUID);
MODULE_AUTHOR("Mario Limonciello <mario.limonciello@dell.com>");
MODULE_DESCRIPTION("Intel WMI Thunderbolt force power driver");
MODULE_LICENSE("GPL");
......@@ -24,6 +24,7 @@
#include <linux/i2c.h>
#include <linux/interrupt.h>
#include <linux/module.h>
#include <linux/regulator/consumer.h>
#include <linux/slab.h>
#define EXPECTED_PTYPE 4
......@@ -34,6 +35,42 @@ struct cht_int33fe_data {
struct i2c_client *pi3usb30532;
};
/*
* Grrr I severly dislike buggy BIOS-es. At least one BIOS enumerates
* the max17047 both through the INT33FE ACPI device (it is right there
* in the resources table) as well as through a separate MAX17047 device.
*
* These helpers are used to work around this by checking if an i2c-client
* for the max17047 has already been registered.
*/
static int cht_int33fe_check_for_max17047(struct device *dev, void *data)
{
struct i2c_client **max17047 = data;
struct acpi_device *adev;
const char *hid;
adev = ACPI_COMPANION(dev);
if (!adev)
return 0;
hid = acpi_device_hid(adev);
/* The MAX17047 ACPI node doesn't have an UID, so we don't check that */
if (strcmp(hid, "MAX17047"))
return 0;
*max17047 = to_i2c_client(dev);
return 1;
}
static struct i2c_client *cht_int33fe_find_max17047(void)
{
struct i2c_client *max17047 = NULL;
i2c_for_each_dev(&max17047, cht_int33fe_check_for_max17047);
return max17047;
}
static const char * const max17047_suppliers[] = { "bq24190-charger" };
static const struct property_entry max17047_props[] = {
......@@ -41,14 +78,25 @@ static const struct property_entry max17047_props[] = {
{ }
};
static const struct property_entry fusb302_props[] = {
PROPERTY_ENTRY_STRING("fcs,extcon-name", "cht_wcove_pwrsrc"),
PROPERTY_ENTRY_U32("fcs,max-sink-microvolt", 12000000),
PROPERTY_ENTRY_U32("fcs,max-sink-microamp", 3000000),
PROPERTY_ENTRY_U32("fcs,max-sink-microwatt", 36000000),
{ }
};
static int cht_int33fe_probe(struct i2c_client *client)
{
struct device *dev = &client->dev;
struct i2c_board_info board_info;
struct cht_int33fe_data *data;
struct i2c_client *max17047;
struct regulator *regulator;
unsigned long long ptyp;
acpi_status status;
int fusb302_irq;
int ret;
status = acpi_evaluate_integer(ACPI_HANDLE(dev), "PTYP", NULL, &ptyp);
if (ACPI_FAILURE(status)) {
......@@ -63,6 +111,34 @@ static int cht_int33fe_probe(struct i2c_client *client)
if (ptyp != EXPECTED_PTYPE)
return -ENODEV;
/* Check presence of INT34D3 (hardware-rev 3) expected for ptype == 4 */
if (!acpi_dev_present("INT34D3", "1", 3)) {
dev_err(dev, "Error PTYPE == %d, but no INT34D3 device\n",
EXPECTED_PTYPE);
return -ENODEV;
}
/*
* We expect the WC PMIC to be paired with a TI bq24292i charger-IC.
* We check for the bq24292i vbus regulator here, this has 2 purposes:
* 1) The bq24292i allows charging with up to 12V, setting the fusb302's
* max-snk voltage to 12V with another charger-IC is not good.
* 2) For the fusb302 driver to get the bq24292i vbus regulator, the
* regulator-map, which is part of the bq24292i regulator_init_data,
* must be registered before the fusb302 is instantiated, otherwise
* it will end up with a dummy-regulator.
* Note "cht_wc_usb_typec_vbus" comes from the regulator_init_data
* which is defined in i2c-cht-wc.c from where the bq24292i i2c-client
* gets instantiated. We use regulator_get_optional here so that we
* don't end up getting a dummy-regulator ourselves.
*/
regulator = regulator_get_optional(dev, "cht_wc_usb_typec_vbus");
if (IS_ERR(regulator)) {
ret = PTR_ERR(regulator);
return (ret == -ENODEV) ? -EPROBE_DEFER : ret;
}
regulator_put(regulator);
/* The FUSB302 uses the irq at index 1 and is the only irq user */
fusb302_irq = acpi_dev_gpio_irq_get(ACPI_COMPANION(dev), 1);
if (fusb302_irq < 0) {
......@@ -75,16 +151,31 @@ static int cht_int33fe_probe(struct i2c_client *client)
if (!data)
return -ENOMEM;
memset(&board_info, 0, sizeof(board_info));
strlcpy(board_info.type, "max17047", I2C_NAME_SIZE);
board_info.properties = max17047_props;
data->max17047 = i2c_acpi_new_device(dev, 1, &board_info);
if (!data->max17047)
return -EPROBE_DEFER; /* Wait for the i2c-adapter to load */
/* Work around BIOS bug, see comment on cht_int33fe_find_max17047 */
max17047 = cht_int33fe_find_max17047();
if (max17047) {
/* Pre-existing i2c-client for the max17047, add device-props */
ret = device_add_properties(&max17047->dev, max17047_props);
if (ret)
return ret;
/* And re-probe to get the new device-props applied. */
ret = device_reprobe(&max17047->dev);
if (ret)
dev_warn(dev, "Reprobing max17047 error: %d\n", ret);
} else {
memset(&board_info, 0, sizeof(board_info));
strlcpy(board_info.type, "max17047", I2C_NAME_SIZE);
board_info.dev_name = "max17047";
board_info.properties = max17047_props;
data->max17047 = i2c_acpi_new_device(dev, 1, &board_info);
if (!data->max17047)
return -EPROBE_DEFER; /* Wait for i2c-adapter to load */
}
memset(&board_info, 0, sizeof(board_info));
strlcpy(board_info.type, "fusb302", I2C_NAME_SIZE);
strlcpy(board_info.type, "typec_fusb302", I2C_NAME_SIZE);
board_info.dev_name = "fusb302";
board_info.properties = fusb302_props;
board_info.irq = fusb302_irq;
data->fusb302 = i2c_acpi_new_device(dev, 2, &board_info);
......@@ -92,6 +183,7 @@ static int cht_int33fe_probe(struct i2c_client *client)
goto out_unregister_max17047;
memset(&board_info, 0, sizeof(board_info));
board_info.dev_name = "pi3usb30532";
strlcpy(board_info.type, "pi3usb30532", I2C_NAME_SIZE);
data->pi3usb30532 = i2c_acpi_new_device(dev, 3, &board_info);
......@@ -106,7 +198,8 @@ static int cht_int33fe_probe(struct i2c_client *client)
i2c_unregister_device(data->fusb302);
out_unregister_max17047:
i2c_unregister_device(data->max17047);
if (data->max17047)
i2c_unregister_device(data->max17047);
return -EPROBE_DEFER; /* Wait for the i2c-adapter to load */
}
......@@ -117,7 +210,8 @@ static int cht_int33fe_remove(struct i2c_client *i2c)
i2c_unregister_device(data->pi3usb30532);
i2c_unregister_device(data->fusb302);
i2c_unregister_device(data->max17047);
if (data->max17047)
i2c_unregister_device(data->max17047);
return 0;
}
......
This diff is collapsed.
......@@ -10,10 +10,6 @@
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
*
* The full GNU General Public License is included in this distribution in
* the file called "COPYING".
*/
......
......@@ -252,28 +252,28 @@ static int intel_punit_get_bars(struct platform_device *pdev)
* - GTDRIVER_IPC BASE_IFACE
*/
res = platform_get_resource(pdev, IORESOURCE_MEM, 2);
if (res) {
if (res && resource_size(res) > 1) {
addr = devm_ioremap_resource(&pdev->dev, res);
if (!IS_ERR(addr))
punit_ipcdev->base[ISPDRIVER_IPC][BASE_DATA] = addr;
}
res = platform_get_resource(pdev, IORESOURCE_MEM, 3);
if (res) {
if (res && resource_size(res) > 1) {
addr = devm_ioremap_resource(&pdev->dev, res);
if (!IS_ERR(addr))
punit_ipcdev->base[ISPDRIVER_IPC][BASE_IFACE] = addr;
}
res = platform_get_resource(pdev, IORESOURCE_MEM, 4);
if (res) {
if (res && resource_size(res) > 1) {
addr = devm_ioremap_resource(&pdev->dev, res);
if (!IS_ERR(addr))
punit_ipcdev->base[GTDRIVER_IPC][BASE_DATA] = addr;
}
res = platform_get_resource(pdev, IORESOURCE_MEM, 5);
if (res) {
if (res && resource_size(res) > 1) {
addr = devm_ioremap_resource(&pdev->dev, res);
if (!IS_ERR(addr))
punit_ipcdev->base[GTDRIVER_IPC][BASE_IFACE] = addr;
......
......@@ -15,9 +15,8 @@
* Telemetry Framework provides platform related PM and performance statistics.
* This file provides the core telemetry API implementation.
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/module.h>
#include <asm/intel_telemetry.h>
......
......@@ -21,14 +21,12 @@
* /sys/kernel/debug/telemetry/ioss_race_verbosity: Write and Change Tracing
* Verbosity via firmware
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/debugfs.h>
#include <linux/seq_file.h>
#include <linux/device.h>
#include <linux/io.h>
#include <linux/uaccess.h>
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/seq_file.h>
#include <linux/suspend.h>
#include <asm/cpu_device_id.h>
......@@ -76,8 +74,6 @@
#define TELEM_IOSS_DX_D0IX_EVTS 25
#define TELEM_IOSS_PG_EVTS 30
#define TELEM_EVT_LEN(x) (sizeof(x)/sizeof((x)[0]))
#define TELEM_DEBUGFS_CPU(model, data) \
{ X86_VENDOR_INTEL, 6, model, X86_FEATURE_ANY, (unsigned long)&data}
......@@ -304,13 +300,13 @@ static struct telemetry_debugfs_conf telem_apl_debugfs_conf = {
.ioss_d0ix_data = telem_apl_ioss_d0ix_data,
.ioss_pg_data = telem_apl_ioss_pg_data,
.pss_idle_evts = TELEM_EVT_LEN(telem_apl_pss_idle_data),
.pcs_idle_blkd_evts = TELEM_EVT_LEN(telem_apl_pcs_idle_blkd_data),
.pcs_s0ix_blkd_evts = TELEM_EVT_LEN(telem_apl_pcs_s0ix_blkd_data),
.pss_ltr_evts = TELEM_EVT_LEN(telem_apl_pss_ltr_data),
.pss_wakeup_evts = TELEM_EVT_LEN(telem_apl_pss_wakeup),
.ioss_d0ix_evts = TELEM_EVT_LEN(telem_apl_ioss_d0ix_data),
.ioss_pg_evts = TELEM_EVT_LEN(telem_apl_ioss_pg_data),
.pss_idle_evts = ARRAY_SIZE(telem_apl_pss_idle_data),
.pcs_idle_blkd_evts = ARRAY_SIZE(telem_apl_pcs_idle_blkd_data),
.pcs_s0ix_blkd_evts = ARRAY_SIZE(telem_apl_pcs_s0ix_blkd_data),
.pss_ltr_evts = ARRAY_SIZE(telem_apl_pss_ltr_data),
.pss_wakeup_evts = ARRAY_SIZE(telem_apl_pss_wakeup),
.ioss_d0ix_evts = ARRAY_SIZE(telem_apl_ioss_d0ix_data),
.ioss_pg_evts = ARRAY_SIZE(telem_apl_ioss_pg_data),
.pstates_id = TELEM_APL_PSS_PSTATES_ID,
.pss_idle_id = TELEM_APL_PSS_IDLE_ID,
......
......@@ -16,15 +16,9 @@
* It used the PUNIT and PMC IPC interfaces for configuring the counters.
* The accumulated results are fetched from SRAM.
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/debugfs.h>
#include <linux/seq_file.h>
#include <linux/io.h>
#include <linux/uaccess.h>
#include <linux/pci.h>
#include <linux/suspend.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <asm/cpu_device_id.h>
......@@ -256,7 +250,7 @@ static int telemetry_check_evtid(enum telemetry_unit telem_unit,
break;
default:
pr_err("Unknown Telemetry action Specified %d\n", action);
pr_err("Unknown Telemetry action specified %d\n", action);
return -EINVAL;
}
......@@ -659,7 +653,7 @@ static int telemetry_setup(struct platform_device *pdev)
ret = telemetry_setup_evtconfig(pss_evtconfig, ioss_evtconfig,
TELEM_RESET);
if (ret) {
dev_err(&pdev->dev, "TELEMTRY Setup Failed\n");
dev_err(&pdev->dev, "TELEMETRY Setup Failed\n");
return ret;
}
return 0;
......@@ -685,7 +679,7 @@ static int telemetry_plt_update_events(struct telemetry_evtconfig pss_evtconfig,
ret = telemetry_setup_evtconfig(pss_evtconfig, ioss_evtconfig,
TELEM_UPDATE);
if (ret)
pr_err("TELEMTRY Config Failed\n");
pr_err("TELEMETRY Config Failed\n");
return ret;
}
......@@ -822,7 +816,7 @@ static int telemetry_plt_reset_events(void)
ret = telemetry_setup_evtconfig(pss_evtconfig, ioss_evtconfig,
TELEM_RESET);
if (ret)
pr_err("TELEMTRY Reset Failed\n");
pr_err("TELEMETRY Reset Failed\n");
return ret;
}
......@@ -885,7 +879,7 @@ static int telemetry_plt_add_events(u8 num_pss_evts, u8 num_ioss_evts,
ret = telemetry_setup_evtconfig(pss_evtconfig, ioss_evtconfig,
TELEM_ADD);
if (ret)
pr_err("TELEMTRY ADD Failed\n");
pr_err("TELEMETRY ADD Failed\n");
return ret;
}
......@@ -1195,7 +1189,7 @@ static int telemetry_pltdrv_probe(struct platform_device *pdev)
ret = telemetry_set_pltdata(&telm_pltops, telm_conf);
if (ret) {
dev_err(&pdev->dev, "TELEMTRY Set Pltops Failed.\n");
dev_err(&pdev->dev, "TELEMETRY Set Pltops Failed.\n");
goto out;
}
......@@ -1210,7 +1204,7 @@ static int telemetry_pltdrv_probe(struct platform_device *pdev)
iounmap(telm_conf->pss_config.regmap);
if (telm_conf->ioss_config.regmap)
iounmap(telm_conf->ioss_config.regmap);
dev_err(&pdev->dev, "TELEMTRY Setup Failed.\n");
dev_err(&pdev->dev, "TELEMETRY Setup Failed.\n");
return ret;
}
......@@ -1234,7 +1228,6 @@ static struct platform_driver telemetry_soc_driver = {
static int __init telemetry_module_init(void)
{
pr_info(DRIVER_NAME ": version %s loaded\n", DRIVER_VERSION);
return platform_driver_register(&telemetry_soc_driver);
}
......
......@@ -125,6 +125,7 @@ static int itmt_legacy_cpu_online(unsigned int cpu)
static const struct x86_cpu_id itmt_legacy_cpu_ids[] = {
ICPU(INTEL_FAM6_BROADWELL_X),
ICPU(INTEL_FAM6_SKYLAKE_X),
{}
};
......
......@@ -216,8 +216,8 @@ static struct resource mlxplat_mlxcpld_resources[] = {
[0] = DEFINE_RES_IRQ_NAMED(17, "mlxcpld-hotplug"),
};
struct platform_device *mlxplat_dev;
struct mlxcpld_hotplug_platform_data *mlxplat_hotplug;
static struct platform_device *mlxplat_dev;
static struct mlxcpld_hotplug_platform_data *mlxplat_hotplug;
static int __init mlxplat_dmi_default_matched(const struct dmi_system_id *dmi)
{
......
......@@ -8,6 +8,7 @@
*/
#include <linux/acpi.h>
#include <linux/dmi.h>
#include <linux/input-polldev.h>
#include <linux/kernel.h>
#include <linux/module.h>
......@@ -64,8 +65,23 @@ static void peaq_wmi_poll(struct input_polled_dev *dev)
}
}
/* Some other devices (Shuttle XS35) use the same WMI GUID for other purposes */
static const struct dmi_system_id peaq_dmi_table[] __initconst = {
{
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "PEAQ"),
DMI_MATCH(DMI_PRODUCT_NAME, "PEAQ PMM C1010 MD99187"),
},
},
{}
};
static int __init peaq_wmi_init(void)
{
/* WMI GUID is not unique, also check for a DMI match */
if (!dmi_check_system(peaq_dmi_table))
return -ENODEV;
if (!wmi_has_guid(PEAQ_DOLBY_BUTTON_GUID))
return -ENODEV;
......@@ -86,9 +102,6 @@ static int __init peaq_wmi_init(void)
static void __exit peaq_wmi_exit(void)
{
if (!wmi_has_guid(PEAQ_DOLBY_BUTTON_GUID))
return;
input_unregister_polled_device(peaq_poll_dev);
}
......
......@@ -58,6 +58,7 @@ static const struct property_entry dexp_ursus_7w_props[] = {
PROPERTY_ENTRY_U32("touchscreen-size-y", 630),
PROPERTY_ENTRY_STRING("firmware-name", "gsl1686-dexp-ursus-7w.fw"),
PROPERTY_ENTRY_U32("silead,max-fingers", 10),
PROPERTY_ENTRY_BOOL("silead,home-button"),
{ }
};
......@@ -72,6 +73,7 @@ static const struct property_entry surftab_wintron70_st70416_6_props[] = {
PROPERTY_ENTRY_STRING("firmware-name",
"gsl1686-surftab-wintron70-st70416-6.fw"),
PROPERTY_ENTRY_U32("silead,max-fingers", 10),
PROPERTY_ENTRY_BOOL("silead,home-button"),
{ }
};
......@@ -83,6 +85,8 @@ static const struct silead_ts_dmi_data surftab_wintron70_st70416_6_data = {
static const struct property_entry gp_electronic_t701_props[] = {
PROPERTY_ENTRY_U32("touchscreen-size-x", 960),
PROPERTY_ENTRY_U32("touchscreen-size-y", 640),
PROPERTY_ENTRY_BOOL("touchscreen-inverted-x"),
PROPERTY_ENTRY_BOOL("touchscreen-inverted-y"),
PROPERTY_ENTRY_STRING("firmware-name",
"gsl1680-gp-electronic-t701.fw"),
{ }
......@@ -114,6 +118,7 @@ static const struct property_entry pov_mobii_wintab_p800w_props[] = {
PROPERTY_ENTRY_BOOL("touchscreen-swapped-x-y"),
PROPERTY_ENTRY_STRING("firmware-name",
"gsl3692-pov-mobii-wintab-p800w.fw"),
PROPERTY_ENTRY_BOOL("silead,home-button"),
{ }
};
......@@ -136,6 +141,36 @@ static const struct silead_ts_dmi_data itworks_tw891_data = {
.properties = itworks_tw891_props,
};
static const struct property_entry chuwi_hi8_pro_props[] = {
PROPERTY_ENTRY_U32("touchscreen-size-x", 1728),
PROPERTY_ENTRY_U32("touchscreen-size-y", 1148),
PROPERTY_ENTRY_BOOL("touchscreen-swapped-x-y"),
PROPERTY_ENTRY_STRING("firmware-name", "gsl3680-chuwi-hi8-pro.fw"),
PROPERTY_ENTRY_BOOL("silead,home-button"),
{ }
};
static const struct silead_ts_dmi_data chuwi_hi8_pro_data = {
.acpi_name = "MSSL1680:00",
.properties = chuwi_hi8_pro_props,
};
static const struct property_entry digma_citi_e200_props[] = {
PROPERTY_ENTRY_U32("touchscreen-size-x", 1980),
PROPERTY_ENTRY_U32("touchscreen-size-y", 1500),
PROPERTY_ENTRY_BOOL("touchscreen-inverted-y"),
PROPERTY_ENTRY_STRING("firmware-name",
"gsl1686-digma_citi_e200.fw"),
PROPERTY_ENTRY_U32("silead,max-fingers", 10),
PROPERTY_ENTRY_BOOL("silead,home-button"),
{ }
};
static const struct silead_ts_dmi_data digma_citi_e200_data = {
.acpi_name = "MSSL1680:00",
.properties = digma_citi_e200_props,
};
static const struct dmi_system_id silead_ts_dmi_table[] = {
{
/* CUBE iwork8 Air */
......@@ -219,6 +254,23 @@ static const struct dmi_system_id silead_ts_dmi_table[] = {
DMI_MATCH(DMI_PRODUCT_NAME, "TW891"),
},
},
{
/* Chuwi Hi8 Pro */
.driver_data = (void *)&chuwi_hi8_pro_data,
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Hampoo"),
DMI_MATCH(DMI_PRODUCT_NAME, "X1D3_C806N"),
},
},
{
/* Digma Citi E200 */
.driver_data = (void *)&digma_citi_e200_data,
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Digma"),
DMI_MATCH(DMI_PRODUCT_NAME, "CITI E200"),
DMI_MATCH(DMI_BOARD_NAME, "Cherry Trail CR"),
},
},
{ },
};
......
......@@ -1627,7 +1627,7 @@ static const struct rfkill_ops sony_rfkill_ops = {
static int sony_nc_setup_rfkill(struct acpi_device *device,
enum sony_nc_rfkill nc_type)
{
int err = 0;
int err;
struct rfkill *rfk;
enum rfkill_type type;
const char *name;
......@@ -1660,17 +1660,19 @@ static int sony_nc_setup_rfkill(struct acpi_device *device,
if (!rfk)
return -ENOMEM;
if (sony_call_snc_handle(sony_rfkill_handle, 0x200, &result) < 0) {
err = sony_call_snc_handle(sony_rfkill_handle, 0x200, &result);
if (err < 0) {
rfkill_destroy(rfk);
return -1;
return err;
}
hwblock = !(result & 0x1);
if (sony_call_snc_handle(sony_rfkill_handle,
sony_rfkill_address[nc_type],
&result) < 0) {
err = sony_call_snc_handle(sony_rfkill_handle,
sony_rfkill_address[nc_type],
&result);
if (err < 0) {
rfkill_destroy(rfk);
return -1;
return err;
}
swblock = !(result & 0x2);
......
......@@ -310,8 +310,7 @@ static struct {
enum {
TP_HOTKEY_TABLET_NONE = 0,
TP_HOTKEY_TABLET_USES_MHKG,
/* X1 Yoga 2016, seen on BIOS N1FET44W */
TP_HOTKEY_TABLET_USES_CMMD,
TP_HOTKEY_TABLET_USES_GMMS,
} hotkey_tablet;
u32 kbdlight:1;
u32 light:1;
......@@ -2044,8 +2043,28 @@ static void hotkey_poll_setup(const bool may_warn);
/* HKEY.MHKG() return bits */
#define TP_HOTKEY_TABLET_MASK (1 << 3)
/* ThinkPad X1 Yoga (2016) */
#define TP_EC_CMMD_TABLET_MODE 0x6
enum {
TP_ACPI_MULTI_MODE_INVALID = 0,
TP_ACPI_MULTI_MODE_UNKNOWN = 1 << 0,
TP_ACPI_MULTI_MODE_LAPTOP = 1 << 1,
TP_ACPI_MULTI_MODE_TABLET = 1 << 2,
TP_ACPI_MULTI_MODE_FLAT = 1 << 3,
TP_ACPI_MULTI_MODE_STAND = 1 << 4,
TP_ACPI_MULTI_MODE_TENT = 1 << 5,
TP_ACPI_MULTI_MODE_STAND_TENT = 1 << 6,
};
enum {
/* The following modes are considered tablet mode for the purpose of
* reporting the status to userspace. i.e. in all these modes it makes
* sense to disable the laptop input devices such as touchpad and
* keyboard.
*/
TP_ACPI_MULTI_MODE_TABLET_LIKE = TP_ACPI_MULTI_MODE_TABLET |
TP_ACPI_MULTI_MODE_STAND |
TP_ACPI_MULTI_MODE_TENT |
TP_ACPI_MULTI_MODE_STAND_TENT,
};
static int hotkey_get_wlsw(void)
{
......@@ -2066,6 +2085,90 @@ static int hotkey_get_wlsw(void)
return (status) ? TPACPI_RFK_RADIO_ON : TPACPI_RFK_RADIO_OFF;
}
static int hotkey_gmms_get_tablet_mode(int s, int *has_tablet_mode)
{
int type = (s >> 16) & 0xffff;
int value = s & 0xffff;
int mode = TP_ACPI_MULTI_MODE_INVALID;
int valid_modes = 0;
if (has_tablet_mode)
*has_tablet_mode = 0;
switch (type) {
case 1:
valid_modes = TP_ACPI_MULTI_MODE_LAPTOP |
TP_ACPI_MULTI_MODE_TABLET |
TP_ACPI_MULTI_MODE_STAND_TENT;
break;
case 2:
valid_modes = TP_ACPI_MULTI_MODE_LAPTOP |
TP_ACPI_MULTI_MODE_FLAT |
TP_ACPI_MULTI_MODE_TABLET |
TP_ACPI_MULTI_MODE_STAND |
TP_ACPI_MULTI_MODE_TENT;
break;
case 3:
valid_modes = TP_ACPI_MULTI_MODE_LAPTOP |
TP_ACPI_MULTI_MODE_FLAT;
break;
case 4:
valid_modes = TP_ACPI_MULTI_MODE_LAPTOP |
TP_ACPI_MULTI_MODE_TABLET |
TP_ACPI_MULTI_MODE_STAND |
TP_ACPI_MULTI_MODE_TENT;
break;
case 5:
valid_modes = TP_ACPI_MULTI_MODE_LAPTOP |
TP_ACPI_MULTI_MODE_FLAT |
TP_ACPI_MULTI_MODE_TABLET |
TP_ACPI_MULTI_MODE_STAND |
TP_ACPI_MULTI_MODE_TENT;
break;
default:
pr_err("Unknown multi mode status type %d with value 0x%04X, please report this to %s\n",
type, value, TPACPI_MAIL);
return 0;
}
if (has_tablet_mode && (valid_modes & TP_ACPI_MULTI_MODE_TABLET_LIKE))
*has_tablet_mode = 1;
switch (value) {
case 1:
mode = TP_ACPI_MULTI_MODE_LAPTOP;
break;
case 2:
mode = TP_ACPI_MULTI_MODE_FLAT;
break;
case 3:
mode = TP_ACPI_MULTI_MODE_TABLET;
break;
case 4:
if (type == 1)
mode = TP_ACPI_MULTI_MODE_STAND_TENT;
else
mode = TP_ACPI_MULTI_MODE_STAND;
break;
case 5:
mode = TP_ACPI_MULTI_MODE_TENT;
break;
default:
if (type == 5 && value == 0xffff) {
pr_warn("Multi mode status is undetected, assuming laptop\n");
return 0;
}
}
if (!(mode & valid_modes)) {
pr_err("Unknown/reserved multi mode value 0x%04X for type %d, please report this to %s\n",
value, type, TPACPI_MAIL);
return 0;
}
return !!(mode & TP_ACPI_MULTI_MODE_TABLET_LIKE);
}
static int hotkey_get_tablet_mode(int *status)
{
int s;
......@@ -2077,11 +2180,11 @@ static int hotkey_get_tablet_mode(int *status)
*status = ((s & TP_HOTKEY_TABLET_MASK) != 0);
break;
case TP_HOTKEY_TABLET_USES_CMMD:
if (!acpi_evalf(ec_handle, &s, "CMMD", "d"))
case TP_HOTKEY_TABLET_USES_GMMS:
if (!acpi_evalf(hkey_handle, &s, "GMMS", "dd", 0))
return -EIO;
*status = (s == TP_EC_CMMD_TABLET_MODE);
*status = hotkey_gmms_get_tablet_mode(s, NULL);
break;
default:
break;
......@@ -3113,16 +3216,19 @@ static int hotkey_init_tablet_mode(void)
int in_tablet_mode = 0, res;
char *type = NULL;
if (acpi_evalf(hkey_handle, &res, "MHKG", "qd")) {
if (acpi_evalf(hkey_handle, &res, "GMMS", "qdd", 0)) {
int has_tablet_mode;
in_tablet_mode = hotkey_gmms_get_tablet_mode(res,
&has_tablet_mode);
if (has_tablet_mode)
tp_features.hotkey_tablet = TP_HOTKEY_TABLET_USES_GMMS;
type = "GMMS";
} else if (acpi_evalf(hkey_handle, &res, "MHKG", "qd")) {
/* For X41t, X60t, X61t Tablets... */
tp_features.hotkey_tablet = TP_HOTKEY_TABLET_USES_MHKG;
in_tablet_mode = !!(res & TP_HOTKEY_TABLET_MASK);
type = "MHKG";
} else if (acpi_evalf(ec_handle, &res, "CMMD", "qd")) {
/* For X1 Yoga (2016) */
tp_features.hotkey_tablet = TP_HOTKEY_TABLET_USES_CMMD;
in_tablet_mode = res == TP_EC_CMMD_TABLET_MODE;
type = "CMMD";
}
if (!tp_features.hotkey_tablet)
......
This diff is collapsed.
......@@ -18,6 +18,7 @@
#include <linux/device.h>
#include <linux/acpi.h>
#include <uapi/linux/wmi.h>
struct wmi_device {
struct device dev;
......@@ -26,13 +27,17 @@ struct wmi_device {
bool setable;
};
/* evaluate the ACPI method associated with this device */
extern acpi_status wmidev_evaluate_method(struct wmi_device *wdev,
u8 instance, u32 method_id,
const struct acpi_buffer *in,
struct acpi_buffer *out);
/* Caller must kfree the result. */
extern union acpi_object *wmidev_block_query(struct wmi_device *wdev,
u8 instance);
/* Gets another device on the same bus. Caller must put_device the result. */
extern struct wmi_device *wmidev_get_other_guid(struct wmi_device *wdev,
const char *guid_string);
extern int set_required_buffer_size(struct wmi_device *wdev, u64 length);
struct wmi_device_id {
const char *guid_string;
......@@ -45,6 +50,8 @@ struct wmi_driver {
int (*probe)(struct wmi_device *wdev);
int (*remove)(struct wmi_device *wdev);
void (*notify)(struct wmi_device *device, union acpi_object *data);
long (*filter_callback)(struct wmi_device *wdev, unsigned int cmd,
struct wmi_ioctl_buffer *arg);
};
extern int __must_check __wmi_driver_register(struct wmi_driver *driver,
......
/*
* User API methods for ACPI-WMI mapping 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 _UAPI_LINUX_WMI_H
#define _UAPI_LINUX_WMI_H
#include <linux/ioctl.h>
#include <linux/types.h>
/* WMI bus will filter all WMI vendor driver requests through this IOC */
#define WMI_IOC 'W'
/* All ioctl requests through WMI should declare their size followed by
* relevant data objects
*/
struct wmi_ioctl_buffer {
__u64 length;
__u8 data[];
};
/* This structure may be modified by the firmware when we enter
* system management mode through SMM, hence the volatiles
*/
struct calling_interface_buffer {
__u16 cmd_class;
__u16 cmd_select;
volatile __u32 input[4];
volatile __u32 output[4];
} __packed;
struct dell_wmi_extensions {
__u32 argattrib;
__u32 blength;
__u8 data[];
} __packed;
struct dell_wmi_smbios_buffer {
__u64 length;
struct calling_interface_buffer std;
struct dell_wmi_extensions ext;
} __packed;
/* Whitelisted smbios class/select commands */
#define CLASS_TOKEN_READ 0
#define CLASS_TOKEN_WRITE 1
#define SELECT_TOKEN_STD 0
#define SELECT_TOKEN_BAT 1
#define SELECT_TOKEN_AC 2
#define CLASS_FLASH_INTERFACE 7
#define SELECT_FLASH_INTERFACE 3
#define CLASS_ADMIN_PROP 10
#define SELECT_ADMIN_PROP 3
#define CLASS_INFO 17
#define SELECT_RFKILL 11
#define SELECT_APP_REGISTRATION 3
#define SELECT_DOCK 22
/* whitelisted tokens */
#define CAPSULE_EN_TOKEN 0x0461
#define CAPSULE_DIS_TOKEN 0x0462
#define WSMT_EN_TOKEN 0x04EC
#define WSMT_DIS_TOKEN 0x04ED
/* Dell SMBIOS calling IOCTL command used by dell-smbios-wmi */
#define DELL_WMI_SMBIOS_CMD _IOWR(WMI_IOC, 0, struct dell_wmi_smbios_buffer)
#endif
......@@ -30,6 +30,7 @@ help:
@echo ' usb - USB testing tools'
@echo ' virtio - vhost test module'
@echo ' vm - misc vm tools'
@echo ' wmi - WMI interface examples'
@echo ' x86_energy_perf_policy - Intel energy policy tool'
@echo ''
@echo 'You can do:'
......@@ -58,7 +59,7 @@ acpi: FORCE
cpupower: FORCE
$(call descend,power/$@)
cgroup firewire hv guest spi usb virtio vm bpf iio gpio objtool leds: FORCE
cgroup firewire hv guest spi usb virtio vm bpf iio gpio objtool leds wmi: FORCE
$(call descend,$@)
liblockdep: FORCE
......@@ -93,7 +94,7 @@ kvm_stat: FORCE
all: acpi cgroup cpupower gpio hv firewire liblockdep \
perf selftests spi turbostat usb \
virtio vm bpf x86_energy_perf_policy \
tmon freefall iio objtool kvm_stat
tmon freefall iio objtool kvm_stat wmi
acpi_install:
$(call descend,power/$(@:_install=),install)
......@@ -101,7 +102,7 @@ acpi_install:
cpupower_install:
$(call descend,power/$(@:_install=),install)
cgroup_install firewire_install gpio_install hv_install iio_install perf_install spi_install usb_install virtio_install vm_install bpf_install objtool_install:
cgroup_install firewire_install gpio_install hv_install iio_install perf_install spi_install usb_install virtio_install vm_install bpf_install objtool_install wmi_install:
$(call descend,$(@:_install=),install)
liblockdep_install:
......@@ -126,7 +127,8 @@ install: acpi_install cgroup_install cpupower_install gpio_install \
hv_install firewire_install iio_install liblockdep_install \
perf_install selftests_install turbostat_install usb_install \
virtio_install vm_install bpf_install x86_energy_perf_policy_install \
tmon_install freefall_install objtool_install kvm_stat_install
tmon_install freefall_install objtool_install kvm_stat_install \
wmi_install
acpi_clean:
$(call descend,power/acpi,clean)
......@@ -134,7 +136,7 @@ acpi_clean:
cpupower_clean:
$(call descend,power/cpupower,clean)
cgroup_clean hv_clean firewire_clean spi_clean usb_clean virtio_clean vm_clean bpf_clean iio_clean gpio_clean objtool_clean leds_clean:
cgroup_clean hv_clean firewire_clean spi_clean usb_clean virtio_clean vm_clean wmi_clean bpf_clean iio_clean gpio_clean objtool_clean leds_clean:
$(call descend,$(@:_clean=),clean)
liblockdep_clean:
......@@ -172,6 +174,6 @@ clean: acpi_clean cgroup_clean cpupower_clean hv_clean firewire_clean \
perf_clean selftests_clean turbostat_clean spi_clean usb_clean virtio_clean \
vm_clean bpf_clean iio_clean x86_energy_perf_policy_clean tmon_clean \
freefall_clean build_clean libbpf_clean libsubcmd_clean liblockdep_clean \
gpio_clean objtool_clean leds_clean
gpio_clean objtool_clean leds_clean wmi_clean
.PHONY: FORCE
PREFIX ?= /usr
SBINDIR ?= sbin
INSTALL ?= install
CFLAGS += -D__EXPORTED_HEADERS__ -I../../include/uapi -I../../include
CC = $(CROSS_COMPILE)gcc
TARGET = dell-smbios-example
all: $(TARGET)
%: %.c
$(CC) $(CFLAGS) $(LDFLAGS) -o $@ $<
clean:
$(RM) $(TARGET)
install: dell-smbios-example
$(INSTALL) -D -m 755 $(TARGET) $(DESTDIR)$(PREFIX)/$(SBINDIR)/$(TARGET)
/*
* Sample application for SMBIOS communication over WMI interface
* Performs the following:
* - Simple cmd_class/cmd_select lookup for TPM information
* - Simple query of known tokens and their values
* - Simple activation of a token
*
* 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.
*/
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/ioctl.h>
#include <unistd.h>
/* if uapi header isn't installed, this might not yet exist */
#ifndef __packed
#define __packed __attribute__((packed))
#endif
#include <linux/wmi.h>
/* It would be better to discover these using udev, but for a simple
* application they're hardcoded
*/
static const char *ioctl_devfs = "/dev/wmi/dell-smbios";
static const char *token_sysfs =
"/sys/bus/platform/devices/dell-smbios.0/tokens";
static void show_buffer(struct dell_wmi_smbios_buffer *buffer)
{
printf("Call: %x/%x [%x,%x,%x,%x]\nResults: [%8x,%8x,%8x,%8x]\n",
buffer->std.cmd_class, buffer->std.cmd_select,
buffer->std.input[0], buffer->std.input[1],
buffer->std.input[2], buffer->std.input[3],
buffer->std.output[0], buffer->std.output[1],
buffer->std.output[2], buffer->std.output[3]);
}
static int run_wmi_smbios_cmd(struct dell_wmi_smbios_buffer *buffer)
{
int fd;
int ret;
fd = open(ioctl_devfs, O_NONBLOCK);
ret = ioctl(fd, DELL_WMI_SMBIOS_CMD, buffer);
close(fd);
return ret;
}
static int find_token(__u16 token, __u16 *location, __u16 *value)
{
char location_sysfs[60];
char value_sysfs[57];
char buf[4096];
FILE *f;
int ret;
ret = sprintf(value_sysfs, "%s/%04x_value", token_sysfs, token);
if (ret < 0) {
printf("sprintf value failed\n");
return 2;
}
f = fopen(value_sysfs, "rb");
if (!f) {
printf("failed to open %s\n", value_sysfs);
return 2;
}
fread(buf, 1, 4096, f);
fclose(f);
*value = (__u16) strtol(buf, NULL, 16);
ret = sprintf(location_sysfs, "%s/%04x_location", token_sysfs, token);
if (ret < 0) {
printf("sprintf location failed\n");
return 1;
}
f = fopen(location_sysfs, "rb");
if (!f) {
printf("failed to open %s\n", location_sysfs);
return 2;
}
fread(buf, 1, 4096, f);
fclose(f);
*location = (__u16) strtol(buf, NULL, 16);
if (*location)
return 0;
return 2;
}
static int token_is_active(__u16 *location, __u16 *cmpvalue,
struct dell_wmi_smbios_buffer *buffer)
{
int ret;
buffer->std.cmd_class = CLASS_TOKEN_READ;
buffer->std.cmd_select = SELECT_TOKEN_STD;
buffer->std.input[0] = *location;
ret = run_wmi_smbios_cmd(buffer);
if (ret != 0 || buffer->std.output[0] != 0)
return ret;
ret = (buffer->std.output[1] == *cmpvalue);
return ret;
}
static int query_token(__u16 token, struct dell_wmi_smbios_buffer *buffer)
{
__u16 location;
__u16 value;
int ret;
ret = find_token(token, &location, &value);
if (ret != 0) {
printf("unable to find token %04x\n", token);
return 1;
}
return token_is_active(&location, &value, buffer);
}
static int activate_token(struct dell_wmi_smbios_buffer *buffer,
__u16 token)
{
__u16 location;
__u16 value;
int ret;
ret = find_token(token, &location, &value);
if (ret != 0) {
printf("unable to find token %04x\n", token);
return 1;
}
buffer->std.cmd_class = CLASS_TOKEN_WRITE;
buffer->std.cmd_select = SELECT_TOKEN_STD;
buffer->std.input[0] = location;
buffer->std.input[1] = 1;
ret = run_wmi_smbios_cmd(buffer);
return ret;
}
static int query_buffer_size(__u64 *buffer_size)
{
FILE *f;
f = fopen(ioctl_devfs, "rb");
if (!f)
return -EINVAL;
fread(buffer_size, sizeof(__u64), 1, f);
fclose(f);
return EXIT_SUCCESS;
}
int main(void)
{
struct dell_wmi_smbios_buffer *buffer;
int ret;
__u64 value = 0;
ret = query_buffer_size(&value);
if (ret == EXIT_FAILURE || !value) {
printf("Unable to read buffer size\n");
goto out;
}
printf("Detected required buffer size %lld\n", value);
buffer = malloc(value);
if (buffer == NULL) {
printf("failed to alloc memory for ioctl\n");
ret = -ENOMEM;
goto out;
}
buffer->length = value;
/* simple SMBIOS call for looking up TPM info */
buffer->std.cmd_class = CLASS_FLASH_INTERFACE;
buffer->std.cmd_select = SELECT_FLASH_INTERFACE;
buffer->std.input[0] = 2;
ret = run_wmi_smbios_cmd(buffer);
if (ret) {
printf("smbios ioctl failed: %d\n", ret);
ret = EXIT_FAILURE;
goto out;
}
show_buffer(buffer);
/* query some tokens */
ret = query_token(CAPSULE_EN_TOKEN, buffer);
printf("UEFI Capsule enabled token is: %d\n", ret);
ret = query_token(CAPSULE_DIS_TOKEN, buffer);
printf("UEFI Capsule disabled token is: %d\n", ret);
/* activate UEFI capsule token if disabled */
if (ret) {
printf("Enabling UEFI capsule token");
if (activate_token(buffer, CAPSULE_EN_TOKEN)) {
printf("activate failed\n");
ret = -1;
goto out;
}
}
ret = EXIT_SUCCESS;
out:
free(buffer);
return ret;
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment