Commit 0dfaeb61 authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'platform-drivers-x86-v4.13-1' of git://git.infradead.org/linux-platform-drivers-x86

Pull x86 platform driver updates from Darren Hart:
 "Introduce new bus architecture for WMI and expose BMOF data through
  sysfs. Correct several assumptions about WMI instance number from 1 to
  0. Further fujitsu-laptop cleanups, continuing to prepare for
  separation into two modules. Add support for several new ideapad
  laptops and silead-based tablets. Various minor fixes and const
  cleanups.

  Detail summary:

  sony-laptop:
   - constify attribute_group and input index array

  fujitsu-laptop:
   - rework debugging
   - do not evaluate ACPI _INI methods
   - do not update ACPI device power status
   - sanitize hotkey input device identification
   - use strcpy to set ACPI device names and classes
   - remove redundant safety checks
   - use device-specific data in remaining module code
   - use device-specific data in LED-related code
   - explicitly pass ACPI device to call_fext_func()
   - track the last instantiated FUJ02E3 ACPI device
   - allocate fujitsu_laptop in acpi_fujitsu_laptop_add()
   - use device-specific data in backlight code
   - allocate fujitsu_bl in acpi_fujitsu_bl_add()
   - distinguish current uses of device-specific data

  msi-laptop:
   - constify msipf*_attribute_group

  eeepc-laptop:
   - constify platform_attribute_group

  toshiba_haps:
   - constify haps_attr_group

  dell-wmi-led:
   - Adjust instance of wmi_evaluate_method calls to 0

  alienware-wmi:
   - Adjust instance of wmi_evaluate_method calls to 0

  intel_menlow:
   - Add const to thermal_cooling_device_ops structure

  acerhdf:
   - Add const to thermal_cooling_device_ops structure

  dell-laptop:
   - Fix bogus keyboard backlight sysfs interface

  acer-wmi:
   - Using zero as first WMI instance number
   - Detect RF Button capability

  ideapad-laptop:
   - Add Y720-15IKBN to no_hw_rfkill
   - Add Y520-15IKBN to no_hw_rfkill
   - constify rfkill_ops structure
   - Squelch ACPI event 1
   - hide unused 'touchpad_store'
   - Switch touchpad attribute to be RO
   - Add sysfs interface for touchpad state

  silead_dmi:
   - Add touchscreen info for PoV mobii wintab p800w
   - Add touchscreen info for Pipo W2S tablet
   - Add touchscreen info for GP-electronic T701

  dell-rbtn:
   - constify rfkill_ops structures
   - Improve explanation about DELLABC6

  samsung-laptop:
   - constify rfkill_ops structures

  panasonic-laptop:
   - remove unused code

  samsung-laptop:
   - Initialize loca variable

  dell-wmi:
   - Convert to the WMI bus infrastructure
   - Add a better description for "stealth mode"
   - Add a comment explaining the 0xb2 magic number

  wmi-bmof:
   - New driver to expose embedded Binary WMI MOF metadata

  wmi*:
   - Fix printing info about WDG structure
   - Add recent copyright statements
   - Require query for data blocks, rename writable to setable
   - Add an interface for subdrivers to access sibling devices
   - Bind the platform device, not the ACPI node
   - Add a new interface to read block data
   - Incorporate acpi_install_notify_handler
   - Instantiate all devices before adding them
   - Probe data objects for read and write capabilities
   - Split devices into types and add basic sysfs attributes
   - Fix error handling when creating devices
   - Turn WMI into a bus driver
   - Track wmi devices per ACPI device
   - Clean up acpi_wmi_add
   - Pass the acpi_device through to parse_wdg
   - Drop "Mapper (un)loaded" messages

  intel_cht_int33fe:
   - Set supplied-from property on max17047 dev

  intel_pmc_ipc:
   - Mark ipc_data_readb() as __maybe_unused

  topstar-laptop:
   - Add new device id

  peaq-wmi:
   - Add new peaq-wmi driver

  thinkpad_acpi:
   - Add a comment about 0 in module_param_call()
   - Join string literals back

  toshiba_acpi:
   - use memdup_user_nul"

* tag 'platform-drivers-x86-v4.13-1' of git://git.infradead.org/linux-platform-drivers-x86: (67 commits)
  platform/x86: sony-laptop: constify attribute_group and input index array
  platform/x86: fujitsu-laptop: rework debugging
  platform/x86: fujitsu-laptop: do not evaluate ACPI _INI methods
  platform/x86: fujitsu-laptop: do not update ACPI device power status
  platform/x86: fujitsu-laptop: sanitize hotkey input device identification
  platform/x86: fujitsu-laptop: use strcpy to set ACPI device names and classes
  platform/x86: fujitsu-laptop: remove redundant safety checks
  platform/x86: msi-laptop: constify msipf*_attribute_group
  platform/x86: eeepc-laptop: constify platform_attribute_group
  platform/x86: toshiba_haps: constify haps_attr_group
  platform/x86: dell-wmi-led: Adjust instance of wmi_evaluate_method calls to 0
  platform/x86: alienware-wmi: Adjust instance of wmi_evaluate_method calls to 0
  platform/x86: intel_menlow: Add const to thermal_cooling_device_ops structure
  platform/x86: acerhdf: Add const to thermal_cooling_device_ops structure
  platform/x86: dell-laptop: Fix bogus keyboard backlight sysfs interface
  platform/x86: acer-wmi: Using zero as first WMI instance number
  platform/x86: ideapad-laptop: Add Y720-15IKBN to no_hw_rfkill
  platform/x86: ideapad-laptop: Add Y520-15IKBN to no_hw_rfkill
  platform/x86: silead_dmi: Add touchscreen info for PoV mobii wintab p800w
  platform/x86: silead_dmi: Add touchscreen info for Pipo W2S tablet
  ...
parents 90311148 d791db9a
...@@ -17,3 +17,11 @@ Description: ...@@ -17,3 +17,11 @@ Description:
* 2 -> Dust Cleaning * 2 -> Dust Cleaning
* 4 -> Efficient Thermal Dissipation Mode * 4 -> Efficient Thermal Dissipation Mode
What: /sys/devices/platform/ideapad/touchpad
Date: May 2017
KernelVersion: 4.13
Contact: "Ritesh Raj Sarraf <rrs@debian.org>"
Description:
Control touchpad mode.
* 1 -> Switched On
* 0 -> Switched Off
...@@ -195,16 +195,6 @@ config FUJITSU_LAPTOP ...@@ -195,16 +195,6 @@ config FUJITSU_LAPTOP
If you have a Fujitsu laptop, say Y or M here. If you have a Fujitsu laptop, say Y or M here.
config FUJITSU_LAPTOP_DEBUG
bool "Verbose debug mode for Fujitsu Laptop Extras"
depends on FUJITSU_LAPTOP
default n
---help---
Enables extra debug output from the fujitsu extras driver, at the
expense of a slight increase in driver size.
If you are not sure, say N here.
config FUJITSU_TABLET config FUJITSU_TABLET
tristate "Fujitsu Tablet Extras" tristate "Fujitsu Tablet Extras"
depends on ACPI depends on ACPI
...@@ -656,6 +646,18 @@ config ACPI_WMI ...@@ -656,6 +646,18 @@ config ACPI_WMI
It is safe to enable this driver even if your DSDT doesn't define It is safe to enable this driver even if your DSDT doesn't define
any ACPI-WMI devices. any ACPI-WMI devices.
config WMI_BMOF
tristate "WMI embedded Binary MOF driver"
depends on ACPI_WMI
default ACPI_WMI
---help---
Say Y here if you want to be able to read a firmware-embedded
WMI Binary MOF data. Using this requires userspace tools and may be
rather tedious.
To compile this driver as a module, choose M here: the module will
be called wmi-bmof.
config MSI_WMI config MSI_WMI
tristate "MSI WMI extras" tristate "MSI WMI extras"
depends on ACPI_WMI depends on ACPI_WMI
...@@ -669,6 +671,13 @@ config MSI_WMI ...@@ -669,6 +671,13 @@ config MSI_WMI
To compile this driver as a module, choose M here: the module will To compile this driver as a module, choose M here: the module will
be called msi-wmi. be called msi-wmi.
config PEAQ_WMI
tristate "PEAQ 2-in-1 WMI hotkey driver"
depends on ACPI_WMI
depends on INPUT
help
Say Y here if you want to support WMI-based hotkeys on PEAQ 2-in-1s.
config TOPSTAR_LAPTOP config TOPSTAR_LAPTOP
tristate "Topstar Laptop Extras" tristate "Topstar Laptop Extras"
depends on ACPI depends on ACPI
......
...@@ -35,8 +35,10 @@ obj-$(CONFIG_PANASONIC_LAPTOP) += panasonic-laptop.o ...@@ -35,8 +35,10 @@ obj-$(CONFIG_PANASONIC_LAPTOP) += panasonic-laptop.o
obj-$(CONFIG_INTEL_MENLOW) += intel_menlow.o obj-$(CONFIG_INTEL_MENLOW) += intel_menlow.o
obj-$(CONFIG_ACPI_WMI) += wmi.o obj-$(CONFIG_ACPI_WMI) += wmi.o
obj-$(CONFIG_MSI_WMI) += msi-wmi.o obj-$(CONFIG_MSI_WMI) += msi-wmi.o
obj-$(CONFIG_PEAQ_WMI) += peaq-wmi.o
obj-$(CONFIG_SURFACE3_WMI) += surface3-wmi.o obj-$(CONFIG_SURFACE3_WMI) += surface3-wmi.o
obj-$(CONFIG_TOPSTAR_LAPTOP) += topstar-laptop.o obj-$(CONFIG_TOPSTAR_LAPTOP) += topstar-laptop.o
obj-$(CONFIG_WMI_BMOF) += wmi-bmof.o
# toshiba_acpi must link after wmi to ensure that wmi devices are found # toshiba_acpi must link after wmi to ensure that wmi devices are found
# before toshiba_acpi initializes # before toshiba_acpi initializes
......
...@@ -149,6 +149,8 @@ struct event_return_value { ...@@ -149,6 +149,8 @@ struct event_return_value {
#define ACER_WMID3_GDS_THREEG (1<<6) /* 3G */ #define ACER_WMID3_GDS_THREEG (1<<6) /* 3G */
#define ACER_WMID3_GDS_WIMAX (1<<7) /* WiMAX */ #define ACER_WMID3_GDS_WIMAX (1<<7) /* WiMAX */
#define ACER_WMID3_GDS_BLUETOOTH (1<<11) /* BT */ #define ACER_WMID3_GDS_BLUETOOTH (1<<11) /* BT */
#define ACER_WMID3_GDS_RFBTN (1<<14) /* RF Button */
#define ACER_WMID3_GDS_TOUCHPAD (1<<1) /* Touchpad */ #define ACER_WMID3_GDS_TOUCHPAD (1<<1) /* Touchpad */
/* Hotkey Customized Setting and Acer Application Status. /* Hotkey Customized Setting and Acer Application Status.
...@@ -221,6 +223,7 @@ struct hotkey_function_type_aa { ...@@ -221,6 +223,7 @@ struct hotkey_function_type_aa {
#define ACER_CAP_BRIGHTNESS (1<<3) #define ACER_CAP_BRIGHTNESS (1<<3)
#define ACER_CAP_THREEG (1<<4) #define ACER_CAP_THREEG (1<<4)
#define ACER_CAP_ACCEL (1<<5) #define ACER_CAP_ACCEL (1<<5)
#define ACER_CAP_RFBTN (1<<6)
#define ACER_CAP_ANY (0xFFFFFFFF) #define ACER_CAP_ANY (0xFFFFFFFF)
/* /*
...@@ -700,7 +703,7 @@ struct acpi_buffer *result) ...@@ -700,7 +703,7 @@ struct acpi_buffer *result)
input.length = sizeof(struct wmab_args); input.length = sizeof(struct wmab_args);
input.pointer = (u8 *)regbuf; input.pointer = (u8 *)regbuf;
status = wmi_evaluate_method(AMW0_GUID1, 1, 1, &input, result); status = wmi_evaluate_method(AMW0_GUID1, 0, 1, &input, result);
return status; return status;
} }
...@@ -965,7 +968,7 @@ WMI_execute_u32(u32 method_id, u32 in, u32 *out) ...@@ -965,7 +968,7 @@ WMI_execute_u32(u32 method_id, u32 in, u32 *out)
u32 tmp = 0; u32 tmp = 0;
acpi_status status; acpi_status status;
status = wmi_evaluate_method(WMID_GUID1, 1, method_id, &input, &result); status = wmi_evaluate_method(WMID_GUID1, 0, method_id, &input, &result);
if (ACPI_FAILURE(status)) if (ACPI_FAILURE(status))
return status; return status;
...@@ -1264,6 +1267,10 @@ static void __init type_aa_dmi_decode(const struct dmi_header *header, void *d) ...@@ -1264,6 +1267,10 @@ static void __init type_aa_dmi_decode(const struct dmi_header *header, void *d)
interface->capability |= ACER_CAP_THREEG; interface->capability |= ACER_CAP_THREEG;
if (type_aa->commun_func_bitmap & ACER_WMID3_GDS_BLUETOOTH) if (type_aa->commun_func_bitmap & ACER_WMID3_GDS_BLUETOOTH)
interface->capability |= ACER_CAP_BLUETOOTH; interface->capability |= ACER_CAP_BLUETOOTH;
if (type_aa->commun_func_bitmap & ACER_WMID3_GDS_RFBTN) {
interface->capability |= ACER_CAP_RFBTN;
commun_func_bitmap &= ~ACER_WMID3_GDS_RFBTN;
}
commun_fn_key_number = type_aa->commun_fn_key_number; commun_fn_key_number = type_aa->commun_fn_key_number;
} }
...@@ -1275,7 +1282,7 @@ static acpi_status __init WMID_set_capabilities(void) ...@@ -1275,7 +1282,7 @@ static acpi_status __init WMID_set_capabilities(void)
acpi_status status; acpi_status status;
u32 devices; u32 devices;
status = wmi_query_block(WMID_GUID2, 1, &out); status = wmi_query_block(WMID_GUID2, 0, &out);
if (ACPI_FAILURE(status)) if (ACPI_FAILURE(status))
return status; return status;
...@@ -2018,7 +2025,7 @@ static u32 get_wmid_devices(void) ...@@ -2018,7 +2025,7 @@ static u32 get_wmid_devices(void)
acpi_status status; acpi_status status;
u32 devices = 0; u32 devices = 0;
status = wmi_query_block(WMID_GUID2, 1, &out); status = wmi_query_block(WMID_GUID2, 0, &out);
if (ACPI_FAILURE(status)) if (ACPI_FAILURE(status))
return 0; return 0;
......
...@@ -557,7 +557,7 @@ static int acerhdf_set_cur_state(struct thermal_cooling_device *cdev, ...@@ -557,7 +557,7 @@ static int acerhdf_set_cur_state(struct thermal_cooling_device *cdev,
} }
/* bind fan callbacks to fan device */ /* bind fan callbacks to fan device */
static struct thermal_cooling_device_ops acerhdf_cooling_ops = { static const struct thermal_cooling_device_ops acerhdf_cooling_ops = {
.get_max_state = acerhdf_get_max_state, .get_max_state = acerhdf_get_max_state,
.get_cur_state = acerhdf_get_cur_state, .get_cur_state = acerhdf_get_cur_state,
.set_cur_state = acerhdf_set_cur_state, .set_cur_state = acerhdf_set_cur_state,
......
...@@ -303,7 +303,7 @@ static int alienware_update_led(struct platform_zone *zone) ...@@ -303,7 +303,7 @@ static int alienware_update_led(struct platform_zone *zone)
} }
pr_debug("alienware-wmi: guid %s method %d\n", guid, method_id); pr_debug("alienware-wmi: guid %s method %d\n", guid, method_id);
status = wmi_evaluate_method(guid, 1, method_id, &input, NULL); status = wmi_evaluate_method(guid, 0, method_id, &input, NULL);
if (ACPI_FAILURE(status)) if (ACPI_FAILURE(status))
pr_err("alienware-wmi: zone set failure: %u\n", status); pr_err("alienware-wmi: zone set failure: %u\n", status);
return ACPI_FAILURE(status); return ACPI_FAILURE(status);
...@@ -352,7 +352,7 @@ static int wmax_brightness(int brightness) ...@@ -352,7 +352,7 @@ static int wmax_brightness(int brightness)
}; };
input.length = (acpi_size) sizeof(args); input.length = (acpi_size) sizeof(args);
input.pointer = &args; input.pointer = &args;
status = wmi_evaluate_method(WMAX_CONTROL_GUID, 1, status = wmi_evaluate_method(WMAX_CONTROL_GUID, 0,
WMAX_METHOD_BRIGHTNESS, &input, NULL); WMAX_METHOD_BRIGHTNESS, &input, NULL);
if (ACPI_FAILURE(status)) if (ACPI_FAILURE(status))
pr_err("alienware-wmi: brightness set failure: %u\n", status); pr_err("alienware-wmi: brightness set failure: %u\n", status);
...@@ -506,10 +506,10 @@ static acpi_status alienware_wmax_command(struct wmax_basic_args *in_args, ...@@ -506,10 +506,10 @@ static acpi_status alienware_wmax_command(struct wmax_basic_args *in_args,
if (out_data != NULL) { if (out_data != NULL) {
output.length = ACPI_ALLOCATE_BUFFER; output.length = ACPI_ALLOCATE_BUFFER;
output.pointer = NULL; output.pointer = NULL;
status = wmi_evaluate_method(WMAX_CONTROL_GUID, 1, status = wmi_evaluate_method(WMAX_CONTROL_GUID, 0,
command, &input, &output); command, &input, &output);
} else } else
status = wmi_evaluate_method(WMAX_CONTROL_GUID, 1, status = wmi_evaluate_method(WMAX_CONTROL_GUID, 0,
command, &input, NULL); command, &input, NULL);
if (ACPI_SUCCESS(status) && out_data != NULL) { if (ACPI_SUCCESS(status) && out_data != NULL) {
......
...@@ -1510,7 +1510,11 @@ static void kbd_init(void) ...@@ -1510,7 +1510,11 @@ static void kbd_init(void)
ret = kbd_init_info(); ret = kbd_init_info();
kbd_init_tokens(); kbd_init_tokens();
if (kbd_token_bits != 0 || ret == 0) /*
* Only supports keyboard backlight when it has at least two modes.
*/
if ((ret == 0 && (kbd_info.levels != 0 || kbd_mode_levels_count >= 2))
|| kbd_get_valid_token_counts() >= 2)
kbd_led_present = true; kbd_led_present = true;
} }
......
...@@ -110,7 +110,7 @@ static int rbtn_rfkill_set_block(void *data, bool blocked) ...@@ -110,7 +110,7 @@ static int rbtn_rfkill_set_block(void *data, bool blocked)
return -EINVAL; return -EINVAL;
} }
static struct rfkill_ops rbtn_ops = { static const struct rfkill_ops rbtn_ops = {
.query = rbtn_rfkill_query, .query = rbtn_rfkill_query,
.set_block = rbtn_rfkill_set_block, .set_block = rbtn_rfkill_set_block,
}; };
...@@ -221,16 +221,27 @@ static const struct acpi_device_id rbtn_ids[] = { ...@@ -221,16 +221,27 @@ static const struct acpi_device_id rbtn_ids[] = {
/* /*
* This driver can also handle the "DELLABC6" device that * This driver can also handle the "DELLABC6" device that
* appears on the XPS 13 9350, but that device is disabled * appears on the XPS 13 9350, but that device is disabled by
* by the DSDT unless booted with acpi_osi="!Windows 2012" * the DSDT unless booted with acpi_osi="!Windows 2012"
* acpi_osi="!Windows 2013". Even if we boot that and bind * acpi_osi="!Windows 2013".
* the driver, we seem to have inconsistent behavior in
* which NetworkManager can get out of sync with the rfkill
* state.
* *
* On the XPS 13 9350 and similar laptops, we're not supposed to * According to Mario at Dell:
* use DELLABC6 at all. Instead, we handle the rfkill button *
* via the intel-hid driver. * DELLABC6 is a custom interface that was created solely to
* have airplane mode support for Windows 7. For Windows 10
* the proper interface is to use that which is handled by
* intel-hid. A OEM airplane mode driver is not used.
*
* Since the kernel doesn't identify as Windows 7 it would be
* incorrect to do attempt to use that interface.
*
* Even if we override _OSI and bind to DELLABC6, we end up with
* inconsistent behavior in which userspace can get out of sync
* with the rfkill state as it conflicts with events from
* intel-hid.
*
* The upshot is that it is better to just ignore DELLABC6
* devices.
*/ */
{ "", 0 }, { "", 0 },
......
...@@ -68,7 +68,7 @@ static int dell_led_perform_fn(u8 length, u8 result_code, u8 device_id, ...@@ -68,7 +68,7 @@ static int dell_led_perform_fn(u8 length, u8 result_code, u8 device_id,
input.length = sizeof(struct bios_args); input.length = sizeof(struct bios_args);
input.pointer = &args; input.pointer = &args;
status = wmi_evaluate_method(DELL_LED_BIOS_GUID, 1, 1, &input, &output); status = wmi_evaluate_method(DELL_LED_BIOS_GUID, 0, 1, &input, &output);
if (ACPI_FAILURE(status)) if (ACPI_FAILURE(status))
return status; return status;
......
...@@ -36,6 +36,7 @@ ...@@ -36,6 +36,7 @@
#include <linux/acpi.h> #include <linux/acpi.h>
#include <linux/string.h> #include <linux/string.h>
#include <linux/dmi.h> #include <linux/dmi.h>
#include <linux/wmi.h>
#include <acpi/video.h> #include <acpi/video.h>
#include "dell-smbios.h" #include "dell-smbios.h"
...@@ -53,6 +54,10 @@ static bool wmi_requires_smbios_request; ...@@ -53,6 +54,10 @@ static bool wmi_requires_smbios_request;
MODULE_ALIAS("wmi:"DELL_EVENT_GUID); MODULE_ALIAS("wmi:"DELL_EVENT_GUID);
MODULE_ALIAS("wmi:"DELL_DESCRIPTOR_GUID); MODULE_ALIAS("wmi:"DELL_DESCRIPTOR_GUID);
struct dell_wmi_priv {
struct input_dev *input_dev;
};
static int __init dmi_matched(const struct dmi_system_id *dmi) static int __init dmi_matched(const struct dmi_system_id *dmi)
{ {
wmi_requires_smbios_request = 1; wmi_requires_smbios_request = 1;
...@@ -86,7 +91,7 @@ static const struct dmi_system_id dell_wmi_smbios_list[] __initconst = { ...@@ -86,7 +91,7 @@ static const struct dmi_system_id dell_wmi_smbios_list[] __initconst = {
* notifications (rather than requests for change) or are also sent * notifications (rather than requests for change) or are also sent
* via the keyboard controller so should not be sent again. * via the keyboard controller so should not be sent again.
*/ */
static const struct key_entry dell_wmi_keymap_type_0000[] __initconst = { static const struct key_entry dell_wmi_keymap_type_0000[] = {
{ KE_IGNORE, 0x003a, { KEY_CAPSLOCK } }, { KE_IGNORE, 0x003a, { KEY_CAPSLOCK } },
/* Key code is followed by brightness level */ /* Key code is followed by brightness level */
...@@ -207,7 +212,7 @@ struct dell_dmi_results { ...@@ -207,7 +212,7 @@ struct dell_dmi_results {
}; };
/* Uninitialized entries here are KEY_RESERVED == 0. */ /* Uninitialized entries here are KEY_RESERVED == 0. */
static const u16 bios_to_linux_keycode[256] __initconst = { static const u16 bios_to_linux_keycode[256] = {
[0] = KEY_MEDIA, [0] = KEY_MEDIA,
[1] = KEY_NEXTSONG, [1] = KEY_NEXTSONG,
[2] = KEY_PLAYPAUSE, [2] = KEY_PLAYPAUSE,
...@@ -256,7 +261,7 @@ static const u16 bios_to_linux_keycode[256] __initconst = { ...@@ -256,7 +261,7 @@ static const u16 bios_to_linux_keycode[256] __initconst = {
* These are applied if the 0xB2 DMI hotkey table is present and doesn't * These are applied if the 0xB2 DMI hotkey table is present and doesn't
* override them. * override them.
*/ */
static const struct key_entry dell_wmi_keymap_type_0010[] __initconst = { static const struct key_entry dell_wmi_keymap_type_0010[] = {
/* Fn-lock */ /* Fn-lock */
{ KE_IGNORE, 0x151, { KEY_RESERVED } }, { KE_IGNORE, 0x151, { KEY_RESERVED } },
...@@ -272,7 +277,12 @@ static const struct key_entry dell_wmi_keymap_type_0010[] __initconst = { ...@@ -272,7 +277,12 @@ static const struct key_entry dell_wmi_keymap_type_0010[] __initconst = {
/* RGB keyboard backlight control */ /* RGB keyboard backlight control */
{ KE_IGNORE, 0x154, { KEY_RESERVED } }, { KE_IGNORE, 0x154, { KEY_RESERVED } },
/* Stealth mode toggle */ /*
* Stealth mode toggle. This will "disable all lights and sounds".
* The action is performed by the BIOS and EC; the WMI event is just
* a notification. On the XPS 13 9350, this is Fn+F7, and there's
* a BIOS setting to enable and disable the hotkey.
*/
{ KE_IGNORE, 0x155, { KEY_RESERVED } }, { KE_IGNORE, 0x155, { KEY_RESERVED } },
/* Rugged magnetic dock attach/detach events */ /* Rugged magnetic dock attach/detach events */
...@@ -289,7 +299,7 @@ static const struct key_entry dell_wmi_keymap_type_0010[] __initconst = { ...@@ -289,7 +299,7 @@ static const struct key_entry dell_wmi_keymap_type_0010[] __initconst = {
/* /*
* Keymap for WMI events of type 0x0011 * Keymap for WMI events of type 0x0011
*/ */
static const struct key_entry dell_wmi_keymap_type_0011[] __initconst = { static const struct key_entry dell_wmi_keymap_type_0011[] = {
/* Battery unplugged */ /* Battery unplugged */
{ KE_IGNORE, 0xfff0, { KEY_RESERVED } }, { KE_IGNORE, 0xfff0, { KEY_RESERVED } },
...@@ -304,13 +314,12 @@ static const struct key_entry dell_wmi_keymap_type_0011[] __initconst = { ...@@ -304,13 +314,12 @@ static const struct key_entry dell_wmi_keymap_type_0011[] __initconst = {
{ KE_IGNORE, 0x02f6, { KEY_RESERVED } }, { KE_IGNORE, 0x02f6, { KEY_RESERVED } },
}; };
static struct input_dev *dell_wmi_input_dev; static void dell_wmi_process_key(struct wmi_device *wdev, int type, int code)
static void dell_wmi_process_key(int type, int code)
{ {
struct dell_wmi_priv *priv = dev_get_drvdata(&wdev->dev);
const struct key_entry *key; const struct key_entry *key;
key = sparse_keymap_entry_from_scancode(dell_wmi_input_dev, key = sparse_keymap_entry_from_scancode(priv->input_dev,
(type << 16) | code); (type << 16) | code);
if (!key) { if (!key) {
pr_info("Unknown key with type 0x%04x and code 0x%04x pressed\n", pr_info("Unknown key with type 0x%04x and code 0x%04x pressed\n",
...@@ -333,33 +342,18 @@ static void dell_wmi_process_key(int type, int code) ...@@ -333,33 +342,18 @@ static void dell_wmi_process_key(int type, int code)
dell_laptop_call_notifier( dell_laptop_call_notifier(
DELL_LAPTOP_KBD_BACKLIGHT_BRIGHTNESS_CHANGED, NULL); DELL_LAPTOP_KBD_BACKLIGHT_BRIGHTNESS_CHANGED, NULL);
sparse_keymap_report_entry(dell_wmi_input_dev, key, 1, true); sparse_keymap_report_entry(priv->input_dev, key, 1, true);
} }
static void dell_wmi_notify(u32 value, void *context) static void dell_wmi_notify(struct wmi_device *wdev,
union acpi_object *obj)
{ {
struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL };
union acpi_object *obj;
acpi_status status;
acpi_size buffer_size;
u16 *buffer_entry, *buffer_end; u16 *buffer_entry, *buffer_end;
acpi_size buffer_size;
int len, i; int len, i;
status = wmi_get_event_data(value, &response);
if (status != AE_OK) {
pr_warn("bad event status 0x%x\n", status);
return;
}
obj = (union acpi_object *)response.pointer;
if (!obj) {
pr_warn("no response\n");
return;
}
if (obj->type != ACPI_TYPE_BUFFER) { if (obj->type != ACPI_TYPE_BUFFER) {
pr_warn("bad response type %x\n", obj->type); pr_warn("bad response type %x\n", obj->type);
kfree(obj);
return; return;
} }
...@@ -404,13 +398,14 @@ static void dell_wmi_notify(u32 value, void *context) ...@@ -404,13 +398,14 @@ static void dell_wmi_notify(u32 value, void *context)
switch (buffer_entry[1]) { switch (buffer_entry[1]) {
case 0x0000: /* One key pressed or event occurred */ case 0x0000: /* One key pressed or event occurred */
if (len > 2) if (len > 2)
dell_wmi_process_key(0x0000, buffer_entry[2]); dell_wmi_process_key(wdev, 0x0000,
buffer_entry[2]);
/* Other entries could contain additional information */ /* Other entries could contain additional information */
break; break;
case 0x0010: /* Sequence of keys pressed */ case 0x0010: /* Sequence of keys pressed */
case 0x0011: /* Sequence of events occurred */ case 0x0011: /* Sequence of events occurred */
for (i = 2; i < len; ++i) for (i = 2; i < len; ++i)
dell_wmi_process_key(buffer_entry[1], dell_wmi_process_key(wdev, buffer_entry[1],
buffer_entry[i]); buffer_entry[i]);
break; break;
default: /* Unknown event */ default: /* Unknown event */
...@@ -423,7 +418,6 @@ static void dell_wmi_notify(u32 value, void *context) ...@@ -423,7 +418,6 @@ static void dell_wmi_notify(u32 value, void *context)
} }
kfree(obj);
} }
static bool have_scancode(u32 scancode, const struct key_entry *keymap, int len) static bool have_scancode(u32 scancode, const struct key_entry *keymap, int len)
...@@ -437,9 +431,7 @@ static bool have_scancode(u32 scancode, const struct key_entry *keymap, int len) ...@@ -437,9 +431,7 @@ static bool have_scancode(u32 scancode, const struct key_entry *keymap, int len)
return false; return false;
} }
static void __init handle_dmi_entry(const struct dmi_header *dm, static void handle_dmi_entry(const struct dmi_header *dm, void *opaque)
void *opaque)
{ {
struct dell_dmi_results *results = opaque; struct dell_dmi_results *results = opaque;
struct dell_bios_hotkey_table *table; struct dell_bios_hotkey_table *table;
...@@ -449,6 +441,7 @@ static void __init handle_dmi_entry(const struct dmi_header *dm, ...@@ -449,6 +441,7 @@ static void __init handle_dmi_entry(const struct dmi_header *dm,
if (results->err || results->keymap) if (results->err || results->keymap)
return; /* We already found the hotkey table. */ return; /* We already found the hotkey table. */
/* The Dell hotkey table is type 0xB2. Scan until we find it. */
if (dm->type != 0xb2) if (dm->type != 0xb2)
return; return;
...@@ -509,19 +502,20 @@ static void __init handle_dmi_entry(const struct dmi_header *dm, ...@@ -509,19 +502,20 @@ static void __init handle_dmi_entry(const struct dmi_header *dm,
results->keymap_size = pos; results->keymap_size = pos;
} }
static int __init dell_wmi_input_setup(void) static int dell_wmi_input_setup(struct wmi_device *wdev)
{ {
struct dell_wmi_priv *priv = dev_get_drvdata(&wdev->dev);
struct dell_dmi_results dmi_results = {}; struct dell_dmi_results dmi_results = {};
struct key_entry *keymap; struct key_entry *keymap;
int err, i, pos = 0; int err, i, pos = 0;
dell_wmi_input_dev = input_allocate_device(); priv->input_dev = input_allocate_device();
if (!dell_wmi_input_dev) if (!priv->input_dev)
return -ENOMEM; return -ENOMEM;
dell_wmi_input_dev->name = "Dell WMI hotkeys"; priv->input_dev->name = "Dell WMI hotkeys";
dell_wmi_input_dev->phys = "wmi/input0"; priv->input_dev->id.bustype = BUS_HOST;
dell_wmi_input_dev->id.bustype = BUS_HOST; priv->input_dev->dev.parent = &wdev->dev;
if (dmi_walk(handle_dmi_entry, &dmi_results)) { if (dmi_walk(handle_dmi_entry, &dmi_results)) {
/* /*
...@@ -596,7 +590,7 @@ static int __init dell_wmi_input_setup(void) ...@@ -596,7 +590,7 @@ static int __init dell_wmi_input_setup(void)
keymap[pos].type = KE_END; keymap[pos].type = KE_END;
err = sparse_keymap_setup(dell_wmi_input_dev, keymap, NULL); err = sparse_keymap_setup(priv->input_dev, keymap, NULL);
/* /*
* Sparse keymap library makes a copy of keymap so we don't need the * Sparse keymap library makes a copy of keymap so we don't need the
* original one that was allocated. * original one that was allocated.
...@@ -605,17 +599,24 @@ static int __init dell_wmi_input_setup(void) ...@@ -605,17 +599,24 @@ static int __init dell_wmi_input_setup(void)
if (err) if (err)
goto err_free_dev; goto err_free_dev;
err = input_register_device(dell_wmi_input_dev); err = input_register_device(priv->input_dev);
if (err) if (err)
goto err_free_dev; goto err_free_dev;
return 0; return 0;
err_free_dev: err_free_dev:
input_free_device(dell_wmi_input_dev); input_free_device(priv->input_dev);
return err; return err;
} }
static void dell_wmi_input_destroy(struct wmi_device *wdev)
{
struct dell_wmi_priv *priv = dev_get_drvdata(&wdev->dev);
input_unregister_device(priv->input_dev);
}
/* /*
* Descriptor buffer is 128 byte long and contains: * Descriptor buffer is 128 byte long and contains:
* *
...@@ -714,46 +715,55 @@ static int dell_wmi_events_set_enabled(bool enable) ...@@ -714,46 +715,55 @@ static int dell_wmi_events_set_enabled(bool enable)
return dell_smbios_error(ret); return dell_smbios_error(ret);
} }
static int dell_wmi_probe(struct wmi_device *wdev)
{
struct dell_wmi_priv *priv = devm_kzalloc(
&wdev->dev, sizeof(struct dell_wmi_priv), GFP_KERNEL);
dev_set_drvdata(&wdev->dev, priv);
return dell_wmi_input_setup(wdev);
}
static int dell_wmi_remove(struct wmi_device *wdev)
{
dell_wmi_input_destroy(wdev);
return 0;
}
static const struct wmi_device_id dell_wmi_id_table[] = {
{ .guid_string = DELL_EVENT_GUID },
{ },
};
static struct wmi_driver dell_wmi_driver = {
.driver = {
.name = "dell-wmi",
},
.id_table = dell_wmi_id_table,
.probe = dell_wmi_probe,
.remove = dell_wmi_remove,
.notify = dell_wmi_notify,
};
static int __init dell_wmi_init(void) static int __init dell_wmi_init(void)
{ {
int err; int err;
acpi_status status;
if (!wmi_has_guid(DELL_EVENT_GUID) ||
!wmi_has_guid(DELL_DESCRIPTOR_GUID)) {
pr_warn("Dell WMI GUID were not found\n");
return -ENODEV;
}
err = dell_wmi_check_descriptor_buffer(); err = dell_wmi_check_descriptor_buffer();
if (err) if (err)
return err; return err;
err = dell_wmi_input_setup();
if (err)
return err;
status = wmi_install_notify_handler(DELL_EVENT_GUID,
dell_wmi_notify, NULL);
if (ACPI_FAILURE(status)) {
input_unregister_device(dell_wmi_input_dev);
pr_err("Unable to register notify handler - %d\n", status);
return -ENODEV;
}
dmi_check_system(dell_wmi_smbios_list); dmi_check_system(dell_wmi_smbios_list);
if (wmi_requires_smbios_request) { if (wmi_requires_smbios_request) {
err = dell_wmi_events_set_enabled(true); err = dell_wmi_events_set_enabled(true);
if (err) { if (err) {
pr_err("Failed to enable WMI events\n"); pr_err("Failed to enable WMI events\n");
wmi_remove_notify_handler(DELL_EVENT_GUID);
input_unregister_device(dell_wmi_input_dev);
return err; return err;
} }
} }
return 0; return wmi_driver_register(&dell_wmi_driver);
} }
module_init(dell_wmi_init); module_init(dell_wmi_init);
...@@ -761,7 +771,7 @@ static void __exit dell_wmi_exit(void) ...@@ -761,7 +771,7 @@ static void __exit dell_wmi_exit(void)
{ {
if (wmi_requires_smbios_request) if (wmi_requires_smbios_request)
dell_wmi_events_set_enabled(false); dell_wmi_events_set_enabled(false);
wmi_remove_notify_handler(DELL_EVENT_GUID);
input_unregister_device(dell_wmi_input_dev); wmi_driver_unregister(&dell_wmi_driver);
} }
module_exit(dell_wmi_exit); module_exit(dell_wmi_exit);
...@@ -445,7 +445,7 @@ static struct attribute *platform_attributes[] = { ...@@ -445,7 +445,7 @@ static struct attribute *platform_attributes[] = {
NULL NULL
}; };
static struct attribute_group platform_attribute_group = { static const struct attribute_group platform_attribute_group = {
.attrs = platform_attributes .attrs = platform_attributes
}; };
......
...@@ -112,25 +112,8 @@ ...@@ -112,25 +112,8 @@
#define MAX_HOTKEY_RINGBUFFER_SIZE 100 #define MAX_HOTKEY_RINGBUFFER_SIZE 100
#define RINGBUFFERSIZE 40 #define RINGBUFFERSIZE 40
/* Debugging */
#define FUJLAPTOP_DBG_ERROR 0x0001
#define FUJLAPTOP_DBG_WARN 0x0002
#define FUJLAPTOP_DBG_INFO 0x0004
#define FUJLAPTOP_DBG_TRACE 0x0008
#ifdef CONFIG_FUJITSU_LAPTOP_DEBUG
#define vdbg_printk(a_dbg_level, format, arg...) \
do { if (dbg_level & a_dbg_level) \
printk(KERN_DEBUG pr_fmt("%s: " format), __func__, ## arg); \
} while (0)
#else
#define vdbg_printk(a_dbg_level, format, arg...) \
do { } while (0)
#endif
/* Device controlling the backlight and associated keys */ /* Device controlling the backlight and associated keys */
struct fujitsu_bl { struct fujitsu_bl {
acpi_handle acpi_handle;
struct input_dev *input; struct input_dev *input;
char phys[32]; char phys[32];
struct backlight_device *bl_device; struct backlight_device *bl_device;
...@@ -144,8 +127,6 @@ static bool disable_brightness_adjust; ...@@ -144,8 +127,6 @@ static bool disable_brightness_adjust;
/* Device used to access hotkeys and other features on the laptop */ /* Device used to access hotkeys and other features on the laptop */
struct fujitsu_laptop { struct fujitsu_laptop {
acpi_handle acpi_handle;
struct acpi_device *dev;
struct input_dev *input; struct input_dev *input;
char phys[32]; char phys[32];
struct platform_device *pf_device; struct platform_device *pf_device;
...@@ -155,15 +136,12 @@ struct fujitsu_laptop { ...@@ -155,15 +136,12 @@ struct fujitsu_laptop {
int flags_state; int flags_state;
}; };
static struct fujitsu_laptop *fujitsu_laptop; static struct acpi_device *fext;
#ifdef CONFIG_FUJITSU_LAPTOP_DEBUG
static u32 dbg_level = 0x03;
#endif
/* Fujitsu ACPI interface function */ /* Fujitsu ACPI interface function */
static int call_fext_func(int func, int op, int feature, int state) static int call_fext_func(struct acpi_device *device,
int func, int op, int feature, int state)
{ {
union acpi_object params[4] = { union acpi_object params[4] = {
{ .integer.type = ACPI_TYPE_INTEGER, .integer.value = func }, { .integer.type = ACPI_TYPE_INTEGER, .integer.value = func },
...@@ -175,28 +153,30 @@ static int call_fext_func(int func, int op, int feature, int state) ...@@ -175,28 +153,30 @@ static int call_fext_func(int func, int op, int feature, int state)
unsigned long long value; unsigned long long value;
acpi_status status; acpi_status status;
status = acpi_evaluate_integer(fujitsu_laptop->acpi_handle, "FUNC", status = acpi_evaluate_integer(device->handle, "FUNC", &arg_list,
&arg_list, &value); &value);
if (ACPI_FAILURE(status)) { if (ACPI_FAILURE(status)) {
vdbg_printk(FUJLAPTOP_DBG_ERROR, "Failed to evaluate FUNC\n"); acpi_handle_err(device->handle, "Failed to evaluate FUNC\n");
return -ENODEV; return -ENODEV;
} }
vdbg_printk(FUJLAPTOP_DBG_TRACE, "FUNC 0x%x (args 0x%x, 0x%x, 0x%x) returned 0x%x\n", acpi_handle_debug(device->handle,
func, op, feature, state, (int)value); "FUNC 0x%x (args 0x%x, 0x%x, 0x%x) returned 0x%x\n",
func, op, feature, state, (int)value);
return value; return value;
} }
/* Hardware access for LCD brightness control */ /* Hardware access for LCD brightness control */
static int set_lcd_level(int level) static int set_lcd_level(struct acpi_device *device, int level)
{ {
struct fujitsu_bl *priv = acpi_driver_data(device);
acpi_status status; acpi_status status;
char *method; char *method;
switch (use_alt_lcd_levels) { switch (use_alt_lcd_levels) {
case -1: case -1:
if (acpi_has_method(fujitsu_bl->acpi_handle, "SBL2")) if (acpi_has_method(device->handle, "SBL2"))
method = "SBL2"; method = "SBL2";
else else
method = "SBLL"; method = "SBLL";
...@@ -209,74 +189,77 @@ static int set_lcd_level(int level) ...@@ -209,74 +189,77 @@ static int set_lcd_level(int level)
break; break;
} }
vdbg_printk(FUJLAPTOP_DBG_TRACE, "set lcd level via %s [%d]\n", acpi_handle_debug(device->handle, "set lcd level via %s [%d]\n", method,
method, level); level);
if (level < 0 || level >= fujitsu_bl->max_brightness) if (level < 0 || level >= priv->max_brightness)
return -EINVAL; return -EINVAL;
status = acpi_execute_simple_method(fujitsu_bl->acpi_handle, method, status = acpi_execute_simple_method(device->handle, method, level);
level);
if (ACPI_FAILURE(status)) { if (ACPI_FAILURE(status)) {
vdbg_printk(FUJLAPTOP_DBG_ERROR, "Failed to evaluate %s\n", acpi_handle_err(device->handle, "Failed to evaluate %s\n",
method); method);
return -ENODEV; return -ENODEV;
} }
fujitsu_bl->brightness_level = level; priv->brightness_level = level;
return 0; return 0;
} }
static int get_lcd_level(void) static int get_lcd_level(struct acpi_device *device)
{ {
struct fujitsu_bl *priv = acpi_driver_data(device);
unsigned long long state = 0; unsigned long long state = 0;
acpi_status status = AE_OK; acpi_status status = AE_OK;
vdbg_printk(FUJLAPTOP_DBG_TRACE, "get lcd level via GBLL\n"); acpi_handle_debug(device->handle, "get lcd level via GBLL\n");
status = acpi_evaluate_integer(fujitsu_bl->acpi_handle, "GBLL", NULL, status = acpi_evaluate_integer(device->handle, "GBLL", NULL, &state);
&state);
if (ACPI_FAILURE(status)) if (ACPI_FAILURE(status))
return 0; return 0;
fujitsu_bl->brightness_level = state & 0x0fffffff; priv->brightness_level = state & 0x0fffffff;
return fujitsu_bl->brightness_level; return priv->brightness_level;
} }
static int get_max_brightness(void) static int get_max_brightness(struct acpi_device *device)
{ {
struct fujitsu_bl *priv = acpi_driver_data(device);
unsigned long long state = 0; unsigned long long state = 0;
acpi_status status = AE_OK; acpi_status status = AE_OK;
vdbg_printk(FUJLAPTOP_DBG_TRACE, "get max lcd level via RBLL\n"); acpi_handle_debug(device->handle, "get max lcd level via RBLL\n");
status = acpi_evaluate_integer(fujitsu_bl->acpi_handle, "RBLL", NULL, status = acpi_evaluate_integer(device->handle, "RBLL", NULL, &state);
&state);
if (ACPI_FAILURE(status)) if (ACPI_FAILURE(status))
return -1; return -1;
fujitsu_bl->max_brightness = state; priv->max_brightness = state;
return fujitsu_bl->max_brightness; return priv->max_brightness;
} }
/* Backlight device stuff */ /* Backlight device stuff */
static int bl_get_brightness(struct backlight_device *b) static int bl_get_brightness(struct backlight_device *b)
{ {
return b->props.power == FB_BLANK_POWERDOWN ? 0 : get_lcd_level(); struct acpi_device *device = bl_get_data(b);
return b->props.power == FB_BLANK_POWERDOWN ? 0 : get_lcd_level(device);
} }
static int bl_update_status(struct backlight_device *b) static int bl_update_status(struct backlight_device *b)
{ {
struct acpi_device *device = bl_get_data(b);
if (b->props.power == FB_BLANK_POWERDOWN) if (b->props.power == FB_BLANK_POWERDOWN)
call_fext_func(FUNC_BACKLIGHT, 0x1, 0x4, 0x3); call_fext_func(fext, FUNC_BACKLIGHT, 0x1, 0x4, 0x3);
else else
call_fext_func(FUNC_BACKLIGHT, 0x1, 0x4, 0x0); call_fext_func(fext, FUNC_BACKLIGHT, 0x1, 0x4, 0x0);
return set_lcd_level(b->props.brightness); return set_lcd_level(device, b->props.brightness);
} }
static const struct backlight_ops fujitsu_bl_ops = { static const struct backlight_ops fujitsu_bl_ops = {
...@@ -287,9 +270,11 @@ static const struct backlight_ops fujitsu_bl_ops = { ...@@ -287,9 +270,11 @@ static const struct backlight_ops fujitsu_bl_ops = {
static ssize_t lid_show(struct device *dev, struct device_attribute *attr, static ssize_t lid_show(struct device *dev, struct device_attribute *attr,
char *buf) char *buf)
{ {
if (!(fujitsu_laptop->flags_supported & FLAG_LID)) struct fujitsu_laptop *priv = dev_get_drvdata(dev);
if (!(priv->flags_supported & FLAG_LID))
return sprintf(buf, "unknown\n"); return sprintf(buf, "unknown\n");
if (fujitsu_laptop->flags_state & FLAG_LID) if (priv->flags_state & FLAG_LID)
return sprintf(buf, "open\n"); return sprintf(buf, "open\n");
else else
return sprintf(buf, "closed\n"); return sprintf(buf, "closed\n");
...@@ -298,9 +283,11 @@ static ssize_t lid_show(struct device *dev, struct device_attribute *attr, ...@@ -298,9 +283,11 @@ static ssize_t lid_show(struct device *dev, struct device_attribute *attr,
static ssize_t dock_show(struct device *dev, struct device_attribute *attr, static ssize_t dock_show(struct device *dev, struct device_attribute *attr,
char *buf) char *buf)
{ {
if (!(fujitsu_laptop->flags_supported & FLAG_DOCK)) struct fujitsu_laptop *priv = dev_get_drvdata(dev);
if (!(priv->flags_supported & FLAG_DOCK))
return sprintf(buf, "unknown\n"); return sprintf(buf, "unknown\n");
if (fujitsu_laptop->flags_state & FLAG_DOCK) if (priv->flags_state & FLAG_DOCK)
return sprintf(buf, "docked\n"); return sprintf(buf, "docked\n");
else else
return sprintf(buf, "undocked\n"); return sprintf(buf, "undocked\n");
...@@ -309,9 +296,11 @@ static ssize_t dock_show(struct device *dev, struct device_attribute *attr, ...@@ -309,9 +296,11 @@ static ssize_t dock_show(struct device *dev, struct device_attribute *attr,
static ssize_t radios_show(struct device *dev, struct device_attribute *attr, static ssize_t radios_show(struct device *dev, struct device_attribute *attr,
char *buf) char *buf)
{ {
if (!(fujitsu_laptop->flags_supported & FLAG_RFKILL)) struct fujitsu_laptop *priv = dev_get_drvdata(dev);
if (!(priv->flags_supported & FLAG_RFKILL))
return sprintf(buf, "unknown\n"); return sprintf(buf, "unknown\n");
if (fujitsu_laptop->flags_state & FLAG_RFKILL) if (priv->flags_state & FLAG_RFKILL)
return sprintf(buf, "on\n"); return sprintf(buf, "on\n");
else else
return sprintf(buf, "killed\n"); return sprintf(buf, "killed\n");
...@@ -348,89 +337,76 @@ static const struct key_entry keymap_backlight[] = { ...@@ -348,89 +337,76 @@ static const struct key_entry keymap_backlight[] = {
static int acpi_fujitsu_bl_input_setup(struct acpi_device *device) static int acpi_fujitsu_bl_input_setup(struct acpi_device *device)
{ {
struct fujitsu_bl *fujitsu_bl = acpi_driver_data(device); struct fujitsu_bl *priv = acpi_driver_data(device);
int ret; int ret;
fujitsu_bl->input = devm_input_allocate_device(&device->dev); priv->input = devm_input_allocate_device(&device->dev);
if (!fujitsu_bl->input) if (!priv->input)
return -ENOMEM; return -ENOMEM;
snprintf(fujitsu_bl->phys, sizeof(fujitsu_bl->phys), snprintf(priv->phys, sizeof(priv->phys), "%s/video/input0",
"%s/video/input0", acpi_device_hid(device)); acpi_device_hid(device));
fujitsu_bl->input->name = acpi_device_name(device); priv->input->name = acpi_device_name(device);
fujitsu_bl->input->phys = fujitsu_bl->phys; priv->input->phys = priv->phys;
fujitsu_bl->input->id.bustype = BUS_HOST; priv->input->id.bustype = BUS_HOST;
fujitsu_bl->input->id.product = 0x06; priv->input->id.product = 0x06;
ret = sparse_keymap_setup(fujitsu_bl->input, keymap_backlight, NULL); ret = sparse_keymap_setup(priv->input, keymap_backlight, NULL);
if (ret) if (ret)
return ret; return ret;
return input_register_device(fujitsu_bl->input); return input_register_device(priv->input);
} }
static int fujitsu_backlight_register(struct acpi_device *device) static int fujitsu_backlight_register(struct acpi_device *device)
{ {
struct fujitsu_bl *priv = acpi_driver_data(device);
const struct backlight_properties props = { const struct backlight_properties props = {
.brightness = fujitsu_bl->brightness_level, .brightness = priv->brightness_level,
.max_brightness = fujitsu_bl->max_brightness - 1, .max_brightness = priv->max_brightness - 1,
.type = BACKLIGHT_PLATFORM .type = BACKLIGHT_PLATFORM
}; };
struct backlight_device *bd; struct backlight_device *bd;
bd = devm_backlight_device_register(&device->dev, "fujitsu-laptop", bd = devm_backlight_device_register(&device->dev, "fujitsu-laptop",
&device->dev, NULL, &device->dev, device,
&fujitsu_bl_ops, &props); &fujitsu_bl_ops, &props);
if (IS_ERR(bd)) if (IS_ERR(bd))
return PTR_ERR(bd); return PTR_ERR(bd);
fujitsu_bl->bl_device = bd; priv->bl_device = bd;
return 0; return 0;
} }
static int acpi_fujitsu_bl_add(struct acpi_device *device) static int acpi_fujitsu_bl_add(struct acpi_device *device)
{ {
int state = 0; struct fujitsu_bl *priv;
int error; int error;
if (acpi_video_get_backlight_type() != acpi_backlight_vendor) if (acpi_video_get_backlight_type() != acpi_backlight_vendor)
return -ENODEV; return -ENODEV;
if (!device) priv = devm_kzalloc(&device->dev, sizeof(*priv), GFP_KERNEL);
return -EINVAL; if (!priv)
return -ENOMEM;
fujitsu_bl->acpi_handle = device->handle; fujitsu_bl = priv;
sprintf(acpi_device_name(device), "%s", ACPI_FUJITSU_BL_DEVICE_NAME); strcpy(acpi_device_name(device), ACPI_FUJITSU_BL_DEVICE_NAME);
sprintf(acpi_device_class(device), "%s", ACPI_FUJITSU_CLASS); strcpy(acpi_device_class(device), ACPI_FUJITSU_CLASS);
device->driver_data = fujitsu_bl; device->driver_data = priv;
error = acpi_fujitsu_bl_input_setup(device); error = acpi_fujitsu_bl_input_setup(device);
if (error) if (error)
return error; return error;
error = acpi_bus_update_power(fujitsu_bl->acpi_handle, &state); pr_info("ACPI: %s [%s]\n",
if (error) { acpi_device_name(device), acpi_device_bid(device));
pr_err("Error reading power state\n");
return error;
}
pr_info("ACPI: %s [%s] (%s)\n",
acpi_device_name(device), acpi_device_bid(device),
!device->power.state ? "on" : "off");
if (acpi_has_method(device->handle, METHOD_NAME__INI)) { if (get_max_brightness(device) <= 0)
vdbg_printk(FUJLAPTOP_DBG_INFO, "Invoking _INI\n"); priv->max_brightness = FUJITSU_LCD_N_LEVELS;
if (ACPI_FAILURE get_lcd_level(device);
(acpi_evaluate_object
(device->handle, METHOD_NAME__INI, NULL, NULL)))
pr_err("_INI Method failed\n");
}
if (get_max_brightness() <= 0)
fujitsu_bl->max_brightness = FUJITSU_LCD_N_LEVELS;
get_lcd_level();
error = fujitsu_backlight_register(device); error = fujitsu_backlight_register(device);
if (error) if (error)
...@@ -443,32 +419,30 @@ static int acpi_fujitsu_bl_add(struct acpi_device *device) ...@@ -443,32 +419,30 @@ static int acpi_fujitsu_bl_add(struct acpi_device *device)
static void acpi_fujitsu_bl_notify(struct acpi_device *device, u32 event) static void acpi_fujitsu_bl_notify(struct acpi_device *device, u32 event)
{ {
struct input_dev *input; struct fujitsu_bl *priv = acpi_driver_data(device);
int oldb, newb; int oldb, newb;
input = fujitsu_bl->input;
if (event != ACPI_FUJITSU_NOTIFY_CODE1) { if (event != ACPI_FUJITSU_NOTIFY_CODE1) {
vdbg_printk(FUJLAPTOP_DBG_WARN, acpi_handle_info(device->handle, "unsupported event [0x%x]\n",
"unsupported event [0x%x]\n", event); event);
sparse_keymap_report_event(input, -1, 1, true); sparse_keymap_report_event(priv->input, -1, 1, true);
return; return;
} }
oldb = fujitsu_bl->brightness_level; oldb = priv->brightness_level;
get_lcd_level(); get_lcd_level(device);
newb = fujitsu_bl->brightness_level; newb = priv->brightness_level;
vdbg_printk(FUJLAPTOP_DBG_TRACE, "brightness button event [%i -> %i]\n", acpi_handle_debug(device->handle,
oldb, newb); "brightness button event [%i -> %i]\n", oldb, newb);
if (oldb == newb) if (oldb == newb)
return; return;
if (!disable_brightness_adjust) if (!disable_brightness_adjust)
set_lcd_level(newb); set_lcd_level(device, newb);
sparse_keymap_report_event(input, oldb < newb, 1, true); sparse_keymap_report_event(priv->input, oldb < newb, 1, true);
} }
/* ACPI device for hotkey handling */ /* ACPI device for hotkey handling */
...@@ -541,42 +515,44 @@ static const struct dmi_system_id fujitsu_laptop_dmi_table[] = { ...@@ -541,42 +515,44 @@ static const struct dmi_system_id fujitsu_laptop_dmi_table[] = {
static int acpi_fujitsu_laptop_input_setup(struct acpi_device *device) static int acpi_fujitsu_laptop_input_setup(struct acpi_device *device)
{ {
struct fujitsu_laptop *fujitsu_laptop = acpi_driver_data(device); struct fujitsu_laptop *priv = acpi_driver_data(device);
int ret; int ret;
fujitsu_laptop->input = devm_input_allocate_device(&device->dev); priv->input = devm_input_allocate_device(&device->dev);
if (!fujitsu_laptop->input) if (!priv->input)
return -ENOMEM; return -ENOMEM;
snprintf(fujitsu_laptop->phys, sizeof(fujitsu_laptop->phys), snprintf(priv->phys, sizeof(priv->phys), "%s/input0",
"%s/video/input0", acpi_device_hid(device)); acpi_device_hid(device));
fujitsu_laptop->input->name = acpi_device_name(device); priv->input->name = acpi_device_name(device);
fujitsu_laptop->input->phys = fujitsu_laptop->phys; priv->input->phys = priv->phys;
fujitsu_laptop->input->id.bustype = BUS_HOST; priv->input->id.bustype = BUS_HOST;
fujitsu_laptop->input->id.product = 0x06;
dmi_check_system(fujitsu_laptop_dmi_table); dmi_check_system(fujitsu_laptop_dmi_table);
ret = sparse_keymap_setup(fujitsu_laptop->input, keymap, NULL); ret = sparse_keymap_setup(priv->input, keymap, NULL);
if (ret) if (ret)
return ret; return ret;
return input_register_device(fujitsu_laptop->input); return input_register_device(priv->input);
} }
static int fujitsu_laptop_platform_add(void) static int fujitsu_laptop_platform_add(struct acpi_device *device)
{ {
struct fujitsu_laptop *priv = acpi_driver_data(device);
int ret; int ret;
fujitsu_laptop->pf_device = platform_device_alloc("fujitsu-laptop", -1); priv->pf_device = platform_device_alloc("fujitsu-laptop", -1);
if (!fujitsu_laptop->pf_device) if (!priv->pf_device)
return -ENOMEM; return -ENOMEM;
ret = platform_device_add(fujitsu_laptop->pf_device); platform_set_drvdata(priv->pf_device, priv);
ret = platform_device_add(priv->pf_device);
if (ret) if (ret)
goto err_put_platform_device; goto err_put_platform_device;
ret = sysfs_create_group(&fujitsu_laptop->pf_device->dev.kobj, ret = sysfs_create_group(&priv->pf_device->dev.kobj,
&fujitsu_pf_attribute_group); &fujitsu_pf_attribute_group);
if (ret) if (ret)
goto err_del_platform_device; goto err_del_platform_device;
...@@ -584,23 +560,26 @@ static int fujitsu_laptop_platform_add(void) ...@@ -584,23 +560,26 @@ static int fujitsu_laptop_platform_add(void)
return 0; return 0;
err_del_platform_device: err_del_platform_device:
platform_device_del(fujitsu_laptop->pf_device); platform_device_del(priv->pf_device);
err_put_platform_device: err_put_platform_device:
platform_device_put(fujitsu_laptop->pf_device); platform_device_put(priv->pf_device);
return ret; return ret;
} }
static void fujitsu_laptop_platform_remove(void) static void fujitsu_laptop_platform_remove(struct acpi_device *device)
{ {
sysfs_remove_group(&fujitsu_laptop->pf_device->dev.kobj, struct fujitsu_laptop *priv = acpi_driver_data(device);
sysfs_remove_group(&priv->pf_device->dev.kobj,
&fujitsu_pf_attribute_group); &fujitsu_pf_attribute_group);
platform_device_unregister(fujitsu_laptop->pf_device); platform_device_unregister(priv->pf_device);
} }
static int logolamp_set(struct led_classdev *cdev, static int logolamp_set(struct led_classdev *cdev,
enum led_brightness brightness) enum led_brightness brightness)
{ {
struct acpi_device *device = to_acpi_device(cdev->dev->parent);
int poweron = FUNC_LED_ON, always = FUNC_LED_ON; int poweron = FUNC_LED_ON, always = FUNC_LED_ON;
int ret; int ret;
...@@ -610,132 +589,128 @@ static int logolamp_set(struct led_classdev *cdev, ...@@ -610,132 +589,128 @@ static int logolamp_set(struct led_classdev *cdev,
if (brightness < LED_FULL) if (brightness < LED_FULL)
always = FUNC_LED_OFF; always = FUNC_LED_OFF;
ret = call_fext_func(FUNC_LEDS, 0x1, LOGOLAMP_POWERON, poweron); ret = call_fext_func(device, FUNC_LEDS, 0x1, LOGOLAMP_POWERON, poweron);
if (ret < 0) if (ret < 0)
return ret; return ret;
return call_fext_func(FUNC_LEDS, 0x1, LOGOLAMP_ALWAYS, always); return call_fext_func(device, FUNC_LEDS, 0x1, LOGOLAMP_ALWAYS, always);
} }
static enum led_brightness logolamp_get(struct led_classdev *cdev) static enum led_brightness logolamp_get(struct led_classdev *cdev)
{ {
struct acpi_device *device = to_acpi_device(cdev->dev->parent);
int ret; int ret;
ret = call_fext_func(FUNC_LEDS, 0x2, LOGOLAMP_ALWAYS, 0x0); ret = call_fext_func(device, FUNC_LEDS, 0x2, LOGOLAMP_ALWAYS, 0x0);
if (ret == FUNC_LED_ON) if (ret == FUNC_LED_ON)
return LED_FULL; return LED_FULL;
ret = call_fext_func(FUNC_LEDS, 0x2, LOGOLAMP_POWERON, 0x0); ret = call_fext_func(device, FUNC_LEDS, 0x2, LOGOLAMP_POWERON, 0x0);
if (ret == FUNC_LED_ON) if (ret == FUNC_LED_ON)
return LED_HALF; return LED_HALF;
return LED_OFF; return LED_OFF;
} }
static struct led_classdev logolamp_led = {
.name = "fujitsu::logolamp",
.brightness_set_blocking = logolamp_set,
.brightness_get = logolamp_get
};
static int kblamps_set(struct led_classdev *cdev, static int kblamps_set(struct led_classdev *cdev,
enum led_brightness brightness) enum led_brightness brightness)
{ {
struct acpi_device *device = to_acpi_device(cdev->dev->parent);
if (brightness >= LED_FULL) if (brightness >= LED_FULL)
return call_fext_func(FUNC_LEDS, 0x1, KEYBOARD_LAMPS, return call_fext_func(device, FUNC_LEDS, 0x1, KEYBOARD_LAMPS,
FUNC_LED_ON); FUNC_LED_ON);
else else
return call_fext_func(FUNC_LEDS, 0x1, KEYBOARD_LAMPS, return call_fext_func(device, FUNC_LEDS, 0x1, KEYBOARD_LAMPS,
FUNC_LED_OFF); FUNC_LED_OFF);
} }
static enum led_brightness kblamps_get(struct led_classdev *cdev) static enum led_brightness kblamps_get(struct led_classdev *cdev)
{ {
struct acpi_device *device = to_acpi_device(cdev->dev->parent);
enum led_brightness brightness = LED_OFF; enum led_brightness brightness = LED_OFF;
if (call_fext_func(FUNC_LEDS, 0x2, KEYBOARD_LAMPS, 0x0) == FUNC_LED_ON) if (call_fext_func(device,
FUNC_LEDS, 0x2, KEYBOARD_LAMPS, 0x0) == FUNC_LED_ON)
brightness = LED_FULL; brightness = LED_FULL;
return brightness; return brightness;
} }
static struct led_classdev kblamps_led = {
.name = "fujitsu::kblamps",
.brightness_set_blocking = kblamps_set,
.brightness_get = kblamps_get
};
static int radio_led_set(struct led_classdev *cdev, static int radio_led_set(struct led_classdev *cdev,
enum led_brightness brightness) enum led_brightness brightness)
{ {
struct acpi_device *device = to_acpi_device(cdev->dev->parent);
if (brightness >= LED_FULL) if (brightness >= LED_FULL)
return call_fext_func(FUNC_FLAGS, 0x5, RADIO_LED_ON, return call_fext_func(device, FUNC_FLAGS, 0x5, RADIO_LED_ON,
RADIO_LED_ON); RADIO_LED_ON);
else else
return call_fext_func(FUNC_FLAGS, 0x5, RADIO_LED_ON, 0x0); return call_fext_func(device, FUNC_FLAGS, 0x5, RADIO_LED_ON,
0x0);
} }
static enum led_brightness radio_led_get(struct led_classdev *cdev) static enum led_brightness radio_led_get(struct led_classdev *cdev)
{ {
struct acpi_device *device = to_acpi_device(cdev->dev->parent);
enum led_brightness brightness = LED_OFF; enum led_brightness brightness = LED_OFF;
if (call_fext_func(FUNC_FLAGS, 0x4, 0x0, 0x0) & RADIO_LED_ON) if (call_fext_func(device, FUNC_FLAGS, 0x4, 0x0, 0x0) & RADIO_LED_ON)
brightness = LED_FULL; brightness = LED_FULL;
return brightness; return brightness;
} }
static struct led_classdev radio_led = {
.name = "fujitsu::radio_led",
.brightness_set_blocking = radio_led_set,
.brightness_get = radio_led_get,
.default_trigger = "rfkill-any"
};
static int eco_led_set(struct led_classdev *cdev, static int eco_led_set(struct led_classdev *cdev,
enum led_brightness brightness) enum led_brightness brightness)
{ {
struct acpi_device *device = to_acpi_device(cdev->dev->parent);
int curr; int curr;
curr = call_fext_func(FUNC_LEDS, 0x2, ECO_LED, 0x0); curr = call_fext_func(device, FUNC_LEDS, 0x2, ECO_LED, 0x0);
if (brightness >= LED_FULL) if (brightness >= LED_FULL)
return call_fext_func(FUNC_LEDS, 0x1, ECO_LED, return call_fext_func(device, FUNC_LEDS, 0x1, ECO_LED,
curr | ECO_LED_ON); curr | ECO_LED_ON);
else else
return call_fext_func(FUNC_LEDS, 0x1, ECO_LED, return call_fext_func(device, FUNC_LEDS, 0x1, ECO_LED,
curr & ~ECO_LED_ON); curr & ~ECO_LED_ON);
} }
static enum led_brightness eco_led_get(struct led_classdev *cdev) static enum led_brightness eco_led_get(struct led_classdev *cdev)
{ {
struct acpi_device *device = to_acpi_device(cdev->dev->parent);
enum led_brightness brightness = LED_OFF; enum led_brightness brightness = LED_OFF;
if (call_fext_func(FUNC_LEDS, 0x2, ECO_LED, 0x0) & ECO_LED_ON) if (call_fext_func(device, FUNC_LEDS, 0x2, ECO_LED, 0x0) & ECO_LED_ON)
brightness = LED_FULL; brightness = LED_FULL;
return brightness; return brightness;
} }
static struct led_classdev eco_led = {
.name = "fujitsu::eco_led",
.brightness_set_blocking = eco_led_set,
.brightness_get = eco_led_get
};
static int acpi_fujitsu_laptop_leds_register(struct acpi_device *device) static int acpi_fujitsu_laptop_leds_register(struct acpi_device *device)
{ {
struct led_classdev *led;
int result; int result;
if (call_fext_func(FUNC_LEDS, 0x0, 0x0, 0x0) & LOGOLAMP_POWERON) { if (call_fext_func(device,
result = devm_led_classdev_register(&device->dev, FUNC_LEDS, 0x0, 0x0, 0x0) & LOGOLAMP_POWERON) {
&logolamp_led); led = devm_kzalloc(&device->dev, sizeof(*led), GFP_KERNEL);
led->name = "fujitsu::logolamp";
led->brightness_set_blocking = logolamp_set;
led->brightness_get = logolamp_get;
result = devm_led_classdev_register(&device->dev, led);
if (result) if (result)
return result; return result;
} }
if ((call_fext_func(FUNC_LEDS, 0x0, 0x0, 0x0) & KEYBOARD_LAMPS) && if ((call_fext_func(device,
(call_fext_func(FUNC_BUTTONS, 0x0, 0x0, 0x0) == 0x0)) { FUNC_LEDS, 0x0, 0x0, 0x0) & KEYBOARD_LAMPS) &&
result = devm_led_classdev_register(&device->dev, &kblamps_led); (call_fext_func(device, FUNC_BUTTONS, 0x0, 0x0, 0x0) == 0x0)) {
led = devm_kzalloc(&device->dev, sizeof(*led), GFP_KERNEL);
led->name = "fujitsu::kblamps";
led->brightness_set_blocking = kblamps_set;
led->brightness_get = kblamps_get;
result = devm_led_classdev_register(&device->dev, led);
if (result) if (result)
return result; return result;
} }
...@@ -746,8 +721,13 @@ static int acpi_fujitsu_laptop_leds_register(struct acpi_device *device) ...@@ -746,8 +721,13 @@ static int acpi_fujitsu_laptop_leds_register(struct acpi_device *device)
* to also have an RF LED. Therefore use bit 24 as an indicator * to also have an RF LED. Therefore use bit 24 as an indicator
* that an RF LED is present. * that an RF LED is present.
*/ */
if (call_fext_func(FUNC_BUTTONS, 0x0, 0x0, 0x0) & BIT(24)) { if (call_fext_func(device, FUNC_BUTTONS, 0x0, 0x0, 0x0) & BIT(24)) {
result = devm_led_classdev_register(&device->dev, &radio_led); led = devm_kzalloc(&device->dev, sizeof(*led), GFP_KERNEL);
led->name = "fujitsu::radio_led";
led->brightness_set_blocking = radio_led_set;
led->brightness_get = radio_led_get;
led->default_trigger = "rfkill-any";
result = devm_led_classdev_register(&device->dev, led);
if (result) if (result)
return result; return result;
} }
...@@ -757,9 +737,14 @@ static int acpi_fujitsu_laptop_leds_register(struct acpi_device *device) ...@@ -757,9 +737,14 @@ static int acpi_fujitsu_laptop_leds_register(struct acpi_device *device)
* bit 14 seems to indicate presence of said led as well. * bit 14 seems to indicate presence of said led as well.
* Confirm by testing the status. * Confirm by testing the status.
*/ */
if ((call_fext_func(FUNC_LEDS, 0x0, 0x0, 0x0) & BIT(14)) && if ((call_fext_func(device, FUNC_LEDS, 0x0, 0x0, 0x0) & BIT(14)) &&
(call_fext_func(FUNC_LEDS, 0x2, ECO_LED, 0x0) != UNSUPPORTED_CMD)) { (call_fext_func(device,
result = devm_led_classdev_register(&device->dev, &eco_led); FUNC_LEDS, 0x2, ECO_LED, 0x0) != UNSUPPORTED_CMD)) {
led = devm_kzalloc(&device->dev, sizeof(*led), GFP_KERNEL);
led->name = "fujitsu::eco_led";
led->brightness_set_blocking = eco_led_set;
led->brightness_get = eco_led_get;
result = devm_led_classdev_register(&device->dev, led);
if (result) if (result)
return result; return result;
} }
...@@ -769,23 +754,25 @@ static int acpi_fujitsu_laptop_leds_register(struct acpi_device *device) ...@@ -769,23 +754,25 @@ static int acpi_fujitsu_laptop_leds_register(struct acpi_device *device)
static int acpi_fujitsu_laptop_add(struct acpi_device *device) static int acpi_fujitsu_laptop_add(struct acpi_device *device)
{ {
int state = 0; struct fujitsu_laptop *priv;
int error; int error;
int i; int i;
if (!device) priv = devm_kzalloc(&device->dev, sizeof(*priv), GFP_KERNEL);
return -EINVAL; if (!priv)
return -ENOMEM;
WARN_ONCE(fext, "More than one FUJ02E3 ACPI device was found. Driver may not work as intended.");
fext = device;
fujitsu_laptop->acpi_handle = device->handle; strcpy(acpi_device_name(device), ACPI_FUJITSU_LAPTOP_DEVICE_NAME);
sprintf(acpi_device_name(device), "%s", strcpy(acpi_device_class(device), ACPI_FUJITSU_CLASS);
ACPI_FUJITSU_LAPTOP_DEVICE_NAME); device->driver_data = priv;
sprintf(acpi_device_class(device), "%s", ACPI_FUJITSU_CLASS);
device->driver_data = fujitsu_laptop;
/* kfifo */ /* kfifo */
spin_lock_init(&fujitsu_laptop->fifo_lock); spin_lock_init(&priv->fifo_lock);
error = kfifo_alloc(&fujitsu_laptop->fifo, RINGBUFFERSIZE * sizeof(int), error = kfifo_alloc(&priv->fifo, RINGBUFFERSIZE * sizeof(int),
GFP_KERNEL); GFP_KERNEL);
if (error) { if (error) {
pr_err("kfifo_alloc failed\n"); pr_err("kfifo_alloc failed\n");
goto err_stop; goto err_stop;
...@@ -795,51 +782,36 @@ static int acpi_fujitsu_laptop_add(struct acpi_device *device) ...@@ -795,51 +782,36 @@ static int acpi_fujitsu_laptop_add(struct acpi_device *device)
if (error) if (error)
goto err_free_fifo; goto err_free_fifo;
error = acpi_bus_update_power(fujitsu_laptop->acpi_handle, &state); pr_info("ACPI: %s [%s]\n",
if (error) { acpi_device_name(device), acpi_device_bid(device));
pr_err("Error reading power state\n");
goto err_free_fifo;
}
pr_info("ACPI: %s [%s] (%s)\n",
acpi_device_name(device), acpi_device_bid(device),
!device->power.state ? "on" : "off");
fujitsu_laptop->dev = device;
if (acpi_has_method(device->handle, METHOD_NAME__INI)) {
vdbg_printk(FUJLAPTOP_DBG_INFO, "Invoking _INI\n");
if (ACPI_FAILURE
(acpi_evaluate_object
(device->handle, METHOD_NAME__INI, NULL, NULL)))
pr_err("_INI Method failed\n");
}
i = 0; i = 0;
while (call_fext_func(FUNC_BUTTONS, 0x1, 0x0, 0x0) != 0 while (call_fext_func(device, FUNC_BUTTONS, 0x1, 0x0, 0x0) != 0
&& (i++) < MAX_HOTKEY_RINGBUFFER_SIZE) && (i++) < MAX_HOTKEY_RINGBUFFER_SIZE)
; /* No action, result is discarded */ ; /* No action, result is discarded */
vdbg_printk(FUJLAPTOP_DBG_INFO, "Discarded %i ringbuffer entries\n", i); acpi_handle_debug(device->handle, "Discarded %i ringbuffer entries\n",
i);
fujitsu_laptop->flags_supported = priv->flags_supported = call_fext_func(device, FUNC_FLAGS, 0x0, 0x0,
call_fext_func(FUNC_FLAGS, 0x0, 0x0, 0x0); 0x0);
/* Make sure our bitmask of supported functions is cleared if the /* Make sure our bitmask of supported functions is cleared if the
RFKILL function block is not implemented, like on the S7020. */ RFKILL function block is not implemented, like on the S7020. */
if (fujitsu_laptop->flags_supported == UNSUPPORTED_CMD) if (priv->flags_supported == UNSUPPORTED_CMD)
fujitsu_laptop->flags_supported = 0; priv->flags_supported = 0;
if (fujitsu_laptop->flags_supported) if (priv->flags_supported)
fujitsu_laptop->flags_state = priv->flags_state = call_fext_func(device, FUNC_FLAGS, 0x4, 0x0,
call_fext_func(FUNC_FLAGS, 0x4, 0x0, 0x0); 0x0);
/* Suspect this is a keymap of the application panel, print it */ /* Suspect this is a keymap of the application panel, print it */
pr_info("BTNI: [0x%x]\n", call_fext_func(FUNC_BUTTONS, 0x0, 0x0, 0x0)); acpi_handle_info(device->handle, "BTNI: [0x%x]\n",
call_fext_func(device, FUNC_BUTTONS, 0x0, 0x0, 0x0));
/* Sync backlight power status */ /* Sync backlight power status */
if (fujitsu_bl->bl_device && if (fujitsu_bl && fujitsu_bl->bl_device &&
acpi_video_get_backlight_type() == acpi_backlight_vendor) { acpi_video_get_backlight_type() == acpi_backlight_vendor) {
if (call_fext_func(FUNC_BACKLIGHT, 0x2, 0x4, 0x0) == 3) if (call_fext_func(fext, FUNC_BACKLIGHT, 0x2, 0x4, 0x0) == 3)
fujitsu_bl->bl_device->props.power = FB_BLANK_POWERDOWN; fujitsu_bl->bl_device->props.power = FB_BLANK_POWERDOWN;
else else
fujitsu_bl->bl_device->props.power = FB_BLANK_UNBLANK; fujitsu_bl->bl_device->props.power = FB_BLANK_UNBLANK;
...@@ -849,103 +821,100 @@ static int acpi_fujitsu_laptop_add(struct acpi_device *device) ...@@ -849,103 +821,100 @@ static int acpi_fujitsu_laptop_add(struct acpi_device *device)
if (error) if (error)
goto err_free_fifo; goto err_free_fifo;
error = fujitsu_laptop_platform_add(); error = fujitsu_laptop_platform_add(device);
if (error) if (error)
goto err_free_fifo; goto err_free_fifo;
return 0; return 0;
err_free_fifo: err_free_fifo:
kfifo_free(&fujitsu_laptop->fifo); kfifo_free(&priv->fifo);
err_stop: err_stop:
return error; return error;
} }
static int acpi_fujitsu_laptop_remove(struct acpi_device *device) static int acpi_fujitsu_laptop_remove(struct acpi_device *device)
{ {
struct fujitsu_laptop *fujitsu_laptop = acpi_driver_data(device); struct fujitsu_laptop *priv = acpi_driver_data(device);
fujitsu_laptop_platform_remove(); fujitsu_laptop_platform_remove(device);
kfifo_free(&fujitsu_laptop->fifo); kfifo_free(&priv->fifo);
return 0; return 0;
} }
static void acpi_fujitsu_laptop_press(int scancode) static void acpi_fujitsu_laptop_press(struct acpi_device *device, int scancode)
{ {
struct input_dev *input = fujitsu_laptop->input; struct fujitsu_laptop *priv = acpi_driver_data(device);
int status; int status;
status = kfifo_in_locked(&fujitsu_laptop->fifo, status = kfifo_in_locked(&priv->fifo, (unsigned char *)&scancode,
(unsigned char *)&scancode, sizeof(scancode), sizeof(scancode), &priv->fifo_lock);
&fujitsu_laptop->fifo_lock);
if (status != sizeof(scancode)) { if (status != sizeof(scancode)) {
vdbg_printk(FUJLAPTOP_DBG_WARN, dev_info(&priv->input->dev, "Could not push scancode [0x%x]\n",
"Could not push scancode [0x%x]\n", scancode); scancode);
return; return;
} }
sparse_keymap_report_event(input, scancode, 1, false); sparse_keymap_report_event(priv->input, scancode, 1, false);
vdbg_printk(FUJLAPTOP_DBG_TRACE, dev_dbg(&priv->input->dev, "Push scancode into ringbuffer [0x%x]\n",
"Push scancode into ringbuffer [0x%x]\n", scancode); scancode);
} }
static void acpi_fujitsu_laptop_release(void) static void acpi_fujitsu_laptop_release(struct acpi_device *device)
{ {
struct input_dev *input = fujitsu_laptop->input; struct fujitsu_laptop *priv = acpi_driver_data(device);
int scancode, status; int scancode, status;
while (true) { while (true) {
status = kfifo_out_locked(&fujitsu_laptop->fifo, status = kfifo_out_locked(&priv->fifo,
(unsigned char *)&scancode, (unsigned char *)&scancode,
sizeof(scancode), sizeof(scancode), &priv->fifo_lock);
&fujitsu_laptop->fifo_lock);
if (status != sizeof(scancode)) if (status != sizeof(scancode))
return; return;
sparse_keymap_report_event(input, scancode, 0, false); sparse_keymap_report_event(priv->input, scancode, 0, false);
vdbg_printk(FUJLAPTOP_DBG_TRACE, dev_dbg(&priv->input->dev,
"Pop scancode from ringbuffer [0x%x]\n", scancode); "Pop scancode from ringbuffer [0x%x]\n", scancode);
} }
} }
static void acpi_fujitsu_laptop_notify(struct acpi_device *device, u32 event) static void acpi_fujitsu_laptop_notify(struct acpi_device *device, u32 event)
{ {
struct input_dev *input; struct fujitsu_laptop *priv = acpi_driver_data(device);
int scancode, i = 0; int scancode, i = 0;
unsigned int irb; unsigned int irb;
input = fujitsu_laptop->input;
if (event != ACPI_FUJITSU_NOTIFY_CODE1) { if (event != ACPI_FUJITSU_NOTIFY_CODE1) {
vdbg_printk(FUJLAPTOP_DBG_WARN, acpi_handle_info(device->handle, "Unsupported event [0x%x]\n",
"Unsupported event [0x%x]\n", event); event);
sparse_keymap_report_event(input, -1, 1, true); sparse_keymap_report_event(priv->input, -1, 1, true);
return; return;
} }
if (fujitsu_laptop->flags_supported) if (priv->flags_supported)
fujitsu_laptop->flags_state = priv->flags_state = call_fext_func(device, FUNC_FLAGS, 0x4, 0x0,
call_fext_func(FUNC_FLAGS, 0x4, 0x0, 0x0); 0x0);
while ((irb = call_fext_func(FUNC_BUTTONS, 0x1, 0x0, 0x0)) != 0 && while ((irb = call_fext_func(device,
FUNC_BUTTONS, 0x1, 0x0, 0x0)) != 0 &&
i++ < MAX_HOTKEY_RINGBUFFER_SIZE) { i++ < MAX_HOTKEY_RINGBUFFER_SIZE) {
scancode = irb & 0x4ff; scancode = irb & 0x4ff;
if (sparse_keymap_entry_from_scancode(input, scancode)) if (sparse_keymap_entry_from_scancode(priv->input, scancode))
acpi_fujitsu_laptop_press(scancode); acpi_fujitsu_laptop_press(device, scancode);
else if (scancode == 0) else if (scancode == 0)
acpi_fujitsu_laptop_release(); acpi_fujitsu_laptop_release(device);
else else
vdbg_printk(FUJLAPTOP_DBG_WARN, acpi_handle_info(device->handle,
"Unknown GIRB result [%x]\n", irb); "Unknown GIRB result [%x]\n", irb);
} }
/* On some models (first seen on the Skylake-based Lifebook /* On some models (first seen on the Skylake-based Lifebook
* E736/E746/E756), the touchpad toggle hotkey (Fn+F4) is * E736/E746/E756), the touchpad toggle hotkey (Fn+F4) is
* handled in software; its state is queried using FUNC_FLAGS * handled in software; its state is queried using FUNC_FLAGS
*/ */
if ((fujitsu_laptop->flags_supported & BIT(26)) && if ((priv->flags_supported & BIT(26)) &&
(call_fext_func(FUNC_FLAGS, 0x1, 0x0, 0x0) & BIT(26))) (call_fext_func(device, FUNC_FLAGS, 0x1, 0x0, 0x0) & BIT(26)))
sparse_keymap_report_event(input, BIT(26), 1, true); sparse_keymap_report_event(priv->input, BIT(26), 1, true);
} }
/* Initialization */ /* Initialization */
...@@ -992,16 +961,9 @@ static int __init fujitsu_init(void) ...@@ -992,16 +961,9 @@ static int __init fujitsu_init(void)
{ {
int ret; int ret;
if (acpi_disabled)
return -ENODEV;
fujitsu_bl = kzalloc(sizeof(struct fujitsu_bl), GFP_KERNEL);
if (!fujitsu_bl)
return -ENOMEM;
ret = acpi_bus_register_driver(&acpi_fujitsu_bl_driver); ret = acpi_bus_register_driver(&acpi_fujitsu_bl_driver);
if (ret) if (ret)
goto err_free_fujitsu_bl; return ret;
/* Register platform stuff */ /* Register platform stuff */
...@@ -1011,28 +973,18 @@ static int __init fujitsu_init(void) ...@@ -1011,28 +973,18 @@ static int __init fujitsu_init(void)
/* Register laptop driver */ /* Register laptop driver */
fujitsu_laptop = kzalloc(sizeof(struct fujitsu_laptop), GFP_KERNEL);
if (!fujitsu_laptop) {
ret = -ENOMEM;
goto err_unregister_platform_driver;
}
ret = acpi_bus_register_driver(&acpi_fujitsu_laptop_driver); ret = acpi_bus_register_driver(&acpi_fujitsu_laptop_driver);
if (ret) if (ret)
goto err_free_fujitsu_laptop; goto err_unregister_platform_driver;
pr_info("driver " FUJITSU_DRIVER_VERSION " successfully loaded\n"); pr_info("driver " FUJITSU_DRIVER_VERSION " successfully loaded\n");
return 0; return 0;
err_free_fujitsu_laptop:
kfree(fujitsu_laptop);
err_unregister_platform_driver: err_unregister_platform_driver:
platform_driver_unregister(&fujitsu_pf_driver); platform_driver_unregister(&fujitsu_pf_driver);
err_unregister_acpi: err_unregister_acpi:
acpi_bus_unregister_driver(&acpi_fujitsu_bl_driver); acpi_bus_unregister_driver(&acpi_fujitsu_bl_driver);
err_free_fujitsu_bl:
kfree(fujitsu_bl);
return ret; return ret;
} }
...@@ -1041,14 +993,10 @@ static void __exit fujitsu_cleanup(void) ...@@ -1041,14 +993,10 @@ static void __exit fujitsu_cleanup(void)
{ {
acpi_bus_unregister_driver(&acpi_fujitsu_laptop_driver); acpi_bus_unregister_driver(&acpi_fujitsu_laptop_driver);
kfree(fujitsu_laptop);
platform_driver_unregister(&fujitsu_pf_driver); platform_driver_unregister(&fujitsu_pf_driver);
acpi_bus_unregister_driver(&acpi_fujitsu_bl_driver); acpi_bus_unregister_driver(&acpi_fujitsu_bl_driver);
kfree(fujitsu_bl);
pr_info("driver unloaded\n"); pr_info("driver unloaded\n");
} }
...@@ -1059,10 +1007,6 @@ module_param(use_alt_lcd_levels, int, 0644); ...@@ -1059,10 +1007,6 @@ module_param(use_alt_lcd_levels, int, 0644);
MODULE_PARM_DESC(use_alt_lcd_levels, "Interface used for setting LCD brightness level (-1 = auto, 0 = force SBLL, 1 = force SBL2)"); MODULE_PARM_DESC(use_alt_lcd_levels, "Interface used for setting LCD brightness level (-1 = auto, 0 = force SBLL, 1 = force SBL2)");
module_param(disable_brightness_adjust, bool, 0644); module_param(disable_brightness_adjust, bool, 0644);
MODULE_PARM_DESC(disable_brightness_adjust, "Disable LCD brightness adjustment"); MODULE_PARM_DESC(disable_brightness_adjust, "Disable LCD brightness adjustment");
#ifdef CONFIG_FUJITSU_LAPTOP_DEBUG
module_param_named(debug, dbg_level, uint, 0644);
MODULE_PARM_DESC(debug, "Sets debug level bit-mask");
#endif
MODULE_AUTHOR("Jonathan Woithe, Peter Gruber, Tony Vroon"); MODULE_AUTHOR("Jonathan Woithe, Peter Gruber, Tony Vroon");
MODULE_DESCRIPTION("Fujitsu laptop extras support"); MODULE_DESCRIPTION("Fujitsu laptop extras support");
......
...@@ -423,9 +423,43 @@ static ssize_t store_ideapad_fan(struct device *dev, ...@@ -423,9 +423,43 @@ static ssize_t store_ideapad_fan(struct device *dev,
static DEVICE_ATTR(fan_mode, 0644, show_ideapad_fan, store_ideapad_fan); static DEVICE_ATTR(fan_mode, 0644, show_ideapad_fan, store_ideapad_fan);
static ssize_t touchpad_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct ideapad_private *priv = dev_get_drvdata(dev);
unsigned long result;
if (read_ec_data(priv->adev->handle, VPCCMD_R_TOUCHPAD, &result))
return sprintf(buf, "-1\n");
return sprintf(buf, "%lu\n", result);
}
/* Switch to RO for now: It might be revisited in the future */
static ssize_t __maybe_unused touchpad_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct ideapad_private *priv = dev_get_drvdata(dev);
bool state;
int ret;
ret = kstrtobool(buf, &state);
if (ret)
return ret;
ret = write_ec_cmd(priv->adev->handle, VPCCMD_W_TOUCHPAD, state);
if (ret < 0)
return -EIO;
return count;
}
static DEVICE_ATTR_RO(touchpad);
static struct attribute *ideapad_attributes[] = { static struct attribute *ideapad_attributes[] = {
&dev_attr_camera_power.attr, &dev_attr_camera_power.attr,
&dev_attr_fan_mode.attr, &dev_attr_fan_mode.attr,
&dev_attr_touchpad.attr,
NULL NULL
}; };
...@@ -478,7 +512,7 @@ static int ideapad_rfk_set(void *data, bool blocked) ...@@ -478,7 +512,7 @@ static int ideapad_rfk_set(void *data, bool blocked)
return write_ec_cmd(priv->priv->adev->handle, opcode, !blocked); return write_ec_cmd(priv->priv->adev->handle, opcode, !blocked);
} }
static struct rfkill_ops ideapad_rfk_ops = { static const struct rfkill_ops ideapad_rfk_ops = {
.set_block = ideapad_rfk_set, .set_block = ideapad_rfk_set,
}; };
...@@ -810,7 +844,6 @@ static void ideapad_acpi_notify(acpi_handle handle, u32 event, void *data) ...@@ -810,7 +844,6 @@ static void ideapad_acpi_notify(acpi_handle handle, u32 event, void *data)
case 8: case 8:
case 7: case 7:
case 6: case 6:
case 1:
ideapad_input_report(priv, vpc_bit); ideapad_input_report(priv, vpc_bit);
break; break;
case 5: case 5:
...@@ -828,6 +861,13 @@ static void ideapad_acpi_notify(acpi_handle handle, u32 event, void *data) ...@@ -828,6 +861,13 @@ static void ideapad_acpi_notify(acpi_handle handle, u32 event, void *data)
case 0: case 0:
ideapad_check_special_buttons(priv); ideapad_check_special_buttons(priv);
break; break;
case 1:
/* Some IdeaPads report event 1 every ~20
* seconds while on battery power; some
* report this when changing to/from tablet
* mode. Squelch this event.
*/
break;
default: default:
pr_info("Unknown event: %lu\n", vpc_bit); pr_info("Unknown event: %lu\n", vpc_bit);
} }
...@@ -910,6 +950,20 @@ static const struct dmi_system_id no_hw_rfkill_list[] = { ...@@ -910,6 +950,20 @@ static const struct dmi_system_id no_hw_rfkill_list[] = {
DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo ideapad Y700-17ISK"), DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo ideapad Y700-17ISK"),
}, },
}, },
{
.ident = "Lenovo Legion Y520-15IKBN",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo Y520-15IKBN"),
},
},
{
.ident = "Lenovo Legion Y720-15IKBN",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo Y720-15IKBN"),
},
},
{ {
.ident = "Lenovo Yoga 2 11 / 13 / Pro", .ident = "Lenovo Yoga 2 11 / 13 / Pro",
.matches = { .matches = {
......
...@@ -34,6 +34,13 @@ struct cht_int33fe_data { ...@@ -34,6 +34,13 @@ struct cht_int33fe_data {
struct i2c_client *pi3usb30532; struct i2c_client *pi3usb30532;
}; };
static const char * const max17047_suppliers[] = { "bq24190-charger" };
static const struct property_entry max17047_props[] = {
PROPERTY_ENTRY_STRING_ARRAY("supplied-from", max17047_suppliers),
{ }
};
static int cht_int33fe_probe(struct i2c_client *client) static int cht_int33fe_probe(struct i2c_client *client)
{ {
struct device *dev = &client->dev; struct device *dev = &client->dev;
...@@ -70,6 +77,7 @@ static int cht_int33fe_probe(struct i2c_client *client) ...@@ -70,6 +77,7 @@ static int cht_int33fe_probe(struct i2c_client *client)
memset(&board_info, 0, sizeof(board_info)); memset(&board_info, 0, sizeof(board_info));
strlcpy(board_info.type, "max17047", I2C_NAME_SIZE); strlcpy(board_info.type, "max17047", I2C_NAME_SIZE);
board_info.properties = max17047_props;
data->max17047 = i2c_acpi_new_device(dev, 1, &board_info); data->max17047 = i2c_acpi_new_device(dev, 1, &board_info);
if (!data->max17047) if (!data->max17047)
......
...@@ -142,7 +142,7 @@ static int memory_set_cur_bandwidth(struct thermal_cooling_device *cdev, ...@@ -142,7 +142,7 @@ static int memory_set_cur_bandwidth(struct thermal_cooling_device *cdev,
return 0; return 0;
} }
static struct thermal_cooling_device_ops memory_cooling_ops = { static const struct thermal_cooling_device_ops memory_cooling_ops = {
.get_max_state = memory_get_max_bandwidth, .get_max_state = memory_get_max_bandwidth,
.get_cur_state = memory_get_cur_bandwidth, .get_cur_state = memory_get_cur_bandwidth,
.set_cur_state = memory_set_cur_bandwidth, .set_cur_state = memory_set_cur_bandwidth,
......
...@@ -186,7 +186,7 @@ static inline void ipc_data_writel(u32 data, u32 offset) ...@@ -186,7 +186,7 @@ static inline void ipc_data_writel(u32 data, u32 offset)
writel(data, ipcdev.ipc_base + IPC_WRITE_BUFFER + offset); writel(data, ipcdev.ipc_base + IPC_WRITE_BUFFER + offset);
} }
static inline u8 ipc_data_readb(u32 offset) static inline u8 __maybe_unused ipc_data_readb(u32 offset)
{ {
return readb(ipcdev.ipc_base + IPC_READ_BUFFER + offset); return readb(ipcdev.ipc_base + IPC_READ_BUFFER + offset);
} }
......
...@@ -563,11 +563,11 @@ static struct attribute *msipf_old_attributes[] = { ...@@ -563,11 +563,11 @@ static struct attribute *msipf_old_attributes[] = {
NULL NULL
}; };
static struct attribute_group msipf_attribute_group = { static const struct attribute_group msipf_attribute_group = {
.attrs = msipf_attributes .attrs = msipf_attributes
}; };
static struct attribute_group msipf_old_attribute_group = { static const struct attribute_group msipf_old_attribute_group = {
.attrs = msipf_old_attributes .attrs = msipf_old_attributes
}; };
......
...@@ -228,10 +228,6 @@ struct pcc_acpi { ...@@ -228,10 +228,6 @@ struct pcc_acpi {
struct backlight_device *backlight; struct backlight_device *backlight;
}; };
struct pcc_keyinput {
struct acpi_hotkey *hotkey;
};
/* method access functions */ /* method access functions */
static int acpi_pcc_write_sset(struct pcc_acpi *pcc, int func, int val) static int acpi_pcc_write_sset(struct pcc_acpi *pcc, int func, int val)
{ {
......
/*
* PEAQ 2-in-1 WMI hotkey driver
* Copyright (C) 2017 Hans de Goede <hdegoede@redhat.com>
*
* 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 <linux/acpi.h>
#include <linux/input-polldev.h>
#include <linux/kernel.h>
#include <linux/module.h>
#define PEAQ_DOLBY_BUTTON_GUID "ABBC0F6F-8EA1-11D1-00A0-C90629100000"
#define PEAQ_DOLBY_BUTTON_METHOD_ID 5
#define PEAQ_POLL_INTERVAL_MS 250
#define PEAQ_POLL_IGNORE_MS 500
#define PEAQ_POLL_MAX_MS 1000
MODULE_ALIAS("wmi:"PEAQ_DOLBY_BUTTON_GUID);
static unsigned int peaq_ignore_events_counter;
static struct input_polled_dev *peaq_poll_dev;
/*
* The Dolby button (yes really a Dolby button) causes an ACPI variable to get
* set on both press and release. The WMI method checks and clears that flag.
* So for a press + release we will get back One from the WMI method either once
* (if polling after the release) or twice (polling between press and release).
* We ignore events for 0.5s after the first event to avoid reporting 2 presses.
*/
static void peaq_wmi_poll(struct input_polled_dev *dev)
{
union acpi_object obj;
acpi_status status;
u32 dummy = 0;
struct acpi_buffer input = { sizeof(dummy), &dummy };
struct acpi_buffer output = { sizeof(obj), &obj };
status = wmi_evaluate_method(PEAQ_DOLBY_BUTTON_GUID, 1,
PEAQ_DOLBY_BUTTON_METHOD_ID,
&input, &output);
if (ACPI_FAILURE(status))
return;
if (obj.type != ACPI_TYPE_INTEGER) {
dev_err(&peaq_poll_dev->input->dev,
"Error WMBC did not return an integer\n");
return;
}
if (peaq_ignore_events_counter && --peaq_ignore_events_counter > 0)
return;
if (obj.integer.value) {
input_event(peaq_poll_dev->input, EV_KEY, KEY_SOUND, 1);
input_sync(peaq_poll_dev->input);
input_event(peaq_poll_dev->input, EV_KEY, KEY_SOUND, 0);
input_sync(peaq_poll_dev->input);
peaq_ignore_events_counter = max(1u,
PEAQ_POLL_IGNORE_MS / peaq_poll_dev->poll_interval);
}
}
static int __init peaq_wmi_init(void)
{
if (!wmi_has_guid(PEAQ_DOLBY_BUTTON_GUID))
return -ENODEV;
peaq_poll_dev = input_allocate_polled_device();
if (!peaq_poll_dev)
return -ENOMEM;
peaq_poll_dev->poll = peaq_wmi_poll;
peaq_poll_dev->poll_interval = PEAQ_POLL_INTERVAL_MS;
peaq_poll_dev->poll_interval_max = PEAQ_POLL_MAX_MS;
peaq_poll_dev->input->name = "PEAQ WMI hotkeys";
peaq_poll_dev->input->phys = "wmi/input0";
peaq_poll_dev->input->id.bustype = BUS_HOST;
input_set_capability(peaq_poll_dev->input, EV_KEY, KEY_SOUND);
return input_register_polled_device(peaq_poll_dev);
}
static void __exit peaq_wmi_exit(void)
{
if (!wmi_has_guid(PEAQ_DOLBY_BUTTON_GUID))
return;
input_unregister_polled_device(peaq_poll_dev);
}
module_init(peaq_wmi_init);
module_exit(peaq_wmi_exit);
MODULE_DESCRIPTION("PEAQ 2-in-1 WMI hotkey driver");
MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>");
MODULE_LICENSE("GPL");
...@@ -591,7 +591,7 @@ static int seclinux_rfkill_set(void *data, bool blocked) ...@@ -591,7 +591,7 @@ static int seclinux_rfkill_set(void *data, bool blocked)
!blocked); !blocked);
} }
static struct rfkill_ops seclinux_rfkill_ops = { static const struct rfkill_ops seclinux_rfkill_ops = {
.set_block = seclinux_rfkill_set, .set_block = seclinux_rfkill_set,
}; };
...@@ -651,7 +651,7 @@ static void swsmi_rfkill_query(struct rfkill *rfkill, void *priv) ...@@ -651,7 +651,7 @@ static void swsmi_rfkill_query(struct rfkill *rfkill, void *priv)
rfkill_set_sw_state(rfkill, !ret); rfkill_set_sw_state(rfkill, !ret);
} }
static struct rfkill_ops swsmi_rfkill_ops = { static const struct rfkill_ops swsmi_rfkill_ops = {
.set_block = swsmi_rfkill_set, .set_block = swsmi_rfkill_set,
.query = swsmi_rfkill_query, .query = swsmi_rfkill_query,
}; };
...@@ -1446,9 +1446,9 @@ static int __init samsung_sabi_init(struct samsung_laptop *samsung) ...@@ -1446,9 +1446,9 @@ static int __init samsung_sabi_init(struct samsung_laptop *samsung)
const struct sabi_config *config = NULL; const struct sabi_config *config = NULL;
const struct sabi_commands *commands; const struct sabi_commands *commands;
unsigned int ifaceP; unsigned int ifaceP;
int loca = 0xffff;
int ret = 0; int ret = 0;
int i; int i;
int loca;
samsung->f0000_segment = ioremap_nocache(0xf0000, 0xffff); samsung->f0000_segment = ioremap_nocache(0xf0000, 0xffff);
if (!samsung->f0000_segment) { if (!samsung->f0000_segment) {
......
...@@ -80,6 +80,48 @@ static const struct silead_ts_dmi_data surftab_wintron70_st70416_6_data = { ...@@ -80,6 +80,48 @@ static const struct silead_ts_dmi_data surftab_wintron70_st70416_6_data = {
.properties = surftab_wintron70_st70416_6_props, .properties = surftab_wintron70_st70416_6_props,
}; };
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_STRING("firmware-name",
"gsl1680-gp-electronic-t701.fw"),
{ }
};
static const struct silead_ts_dmi_data gp_electronic_t701_data = {
.acpi_name = "MSSL1680:00",
.properties = gp_electronic_t701_props,
};
static const struct property_entry pipo_w2s_props[] = {
PROPERTY_ENTRY_U32("touchscreen-size-x", 1660),
PROPERTY_ENTRY_U32("touchscreen-size-y", 880),
PROPERTY_ENTRY_BOOL("touchscreen-inverted-x"),
PROPERTY_ENTRY_BOOL("touchscreen-swapped-x-y"),
PROPERTY_ENTRY_STRING("firmware-name",
"gsl1680-pipo-w2s.fw"),
{ }
};
static const struct silead_ts_dmi_data pipo_w2s_data = {
.acpi_name = "MSSL1680:00",
.properties = pipo_w2s_props,
};
static const struct property_entry pov_mobii_wintab_p800w_props[] = {
PROPERTY_ENTRY_U32("touchscreen-size-x", 1800),
PROPERTY_ENTRY_U32("touchscreen-size-y", 1150),
PROPERTY_ENTRY_BOOL("touchscreen-swapped-x-y"),
PROPERTY_ENTRY_STRING("firmware-name",
"gsl3692-pov-mobii-wintab-p800w.fw"),
{ }
};
static const struct silead_ts_dmi_data pov_mobii_wintab_p800w_data = {
.acpi_name = "MSSL1680:00",
.properties = pov_mobii_wintab_p800w_props,
};
static const struct dmi_system_id silead_ts_dmi_table[] = { static const struct dmi_system_id silead_ts_dmi_table[] = {
{ {
/* CUBE iwork8 Air */ /* CUBE iwork8 Air */
...@@ -117,6 +159,34 @@ static const struct dmi_system_id silead_ts_dmi_table[] = { ...@@ -117,6 +159,34 @@ static const struct dmi_system_id silead_ts_dmi_table[] = {
DMI_MATCH(DMI_BIOS_VERSION, "TREK.G.WI71C.JGBMRBA04"), DMI_MATCH(DMI_BIOS_VERSION, "TREK.G.WI71C.JGBMRBA04"),
}, },
}, },
{
/* GP-electronic T701 */
.driver_data = (void *)&gp_electronic_t701_data,
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Insyde"),
DMI_MATCH(DMI_PRODUCT_NAME, "T701"),
DMI_MATCH(DMI_BIOS_VERSION, "BYT70A.YNCHENG.WIN.007"),
},
},
{
/* Pipo W2S */
.driver_data = (void *)&pipo_w2s_data,
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "PIPO"),
DMI_MATCH(DMI_PRODUCT_NAME, "W2S"),
},
},
{
/* Point of View mobii wintab p800w */
.driver_data = (void *)&pov_mobii_wintab_p800w_data,
.matches = {
DMI_MATCH(DMI_BOARD_VENDOR, "AMI Corporation"),
DMI_MATCH(DMI_BOARD_NAME, "Aptio CRB"),
DMI_MATCH(DMI_BIOS_VERSION, "3BAIR1013"),
/* Above matches are too generic, add bios-date match */
DMI_MATCH(DMI_BIOS_DATE, "08/22/2014"),
},
},
{ }, { },
}; };
......
...@@ -222,7 +222,7 @@ struct sony_laptop_keypress { ...@@ -222,7 +222,7 @@ struct sony_laptop_keypress {
/* Correspondance table between sonypi events /* Correspondance table between sonypi events
* and input layer indexes in the keymap * and input layer indexes in the keymap
*/ */
static int sony_laptop_input_index[] = { static const int sony_laptop_input_index[] = {
-1, /* 0 no event */ -1, /* 0 no event */
-1, /* 1 SONYPI_EVENT_JOGDIAL_DOWN */ -1, /* 1 SONYPI_EVENT_JOGDIAL_DOWN */
-1, /* 2 SONYPI_EVENT_JOGDIAL_UP */ -1, /* 2 SONYPI_EVENT_JOGDIAL_UP */
...@@ -4032,7 +4032,7 @@ static struct attribute *spic_attributes[] = { ...@@ -4032,7 +4032,7 @@ static struct attribute *spic_attributes[] = {
NULL NULL
}; };
static struct attribute_group spic_attribute_group = { static const struct attribute_group spic_attribute_group = {
.attrs = spic_attributes .attrs = spic_attributes
}; };
......
...@@ -590,8 +590,8 @@ static int acpi_evalf(acpi_handle handle, ...@@ -590,8 +590,8 @@ static int acpi_evalf(acpi_handle handle,
break; break;
/* add more types as needed */ /* add more types as needed */
default: default:
pr_err("acpi_evalf() called " pr_err("acpi_evalf() called with invalid format character '%c'\n",
"with invalid format character '%c'\n", c); c);
va_end(ap); va_end(ap);
return 0; return 0;
} }
...@@ -619,8 +619,8 @@ static int acpi_evalf(acpi_handle handle, ...@@ -619,8 +619,8 @@ static int acpi_evalf(acpi_handle handle,
break; break;
/* add more types as needed */ /* add more types as needed */
default: default:
pr_err("acpi_evalf() called " pr_err("acpi_evalf() called with invalid format character '%c'\n",
"with invalid format character '%c'\n", res_type); res_type);
return 0; return 0;
} }
...@@ -790,8 +790,8 @@ static int __init setup_acpi_notify(struct ibm_struct *ibm) ...@@ -790,8 +790,8 @@ static int __init setup_acpi_notify(struct ibm_struct *ibm)
ibm->acpi->type, dispatch_acpi_notify, ibm); ibm->acpi->type, dispatch_acpi_notify, ibm);
if (ACPI_FAILURE(status)) { if (ACPI_FAILURE(status)) {
if (status == AE_ALREADY_EXISTS) { if (status == AE_ALREADY_EXISTS) {
pr_notice("another device driver is already " pr_notice("another device driver is already handling %s events\n",
"handling %s events\n", ibm->name); ibm->name);
} else { } else {
pr_err("acpi_install_notify_handler(%s) failed: %s\n", pr_err("acpi_install_notify_handler(%s) failed: %s\n",
ibm->name, acpi_format_exception(status)); ibm->name, acpi_format_exception(status));
...@@ -1095,8 +1095,7 @@ static void printk_deprecated_attribute(const char * const what, ...@@ -1095,8 +1095,7 @@ static void printk_deprecated_attribute(const char * const what,
const char * const details) const char * const details)
{ {
tpacpi_log_usertask("deprecated sysfs attribute"); tpacpi_log_usertask("deprecated sysfs attribute");
pr_warn("WARNING: sysfs attribute %s is deprecated and " pr_warn("WARNING: sysfs attribute %s is deprecated and will be removed. %s\n",
"will be removed. %s\n",
what, details); what, details);
} }
...@@ -1796,8 +1795,7 @@ static void __init tpacpi_check_outdated_fw(void) ...@@ -1796,8 +1795,7 @@ static void __init tpacpi_check_outdated_fw(void)
* best if the user upgrades the firmware anyway. * best if the user upgrades the firmware anyway.
*/ */
pr_warn("WARNING: Outdated ThinkPad BIOS/EC firmware\n"); pr_warn("WARNING: Outdated ThinkPad BIOS/EC firmware\n");
pr_warn("WARNING: This firmware may be missing critical bug " pr_warn("WARNING: This firmware may be missing critical bug fixes and/or important features\n");
"fixes and/or important features\n");
} }
} }
...@@ -2166,8 +2164,7 @@ static int hotkey_mask_set(u32 mask) ...@@ -2166,8 +2164,7 @@ static int hotkey_mask_set(u32 mask)
* a given event. * a given event.
*/ */
if (!hotkey_mask_get() && !rc && (fwmask & ~hotkey_acpi_mask)) { if (!hotkey_mask_get() && !rc && (fwmask & ~hotkey_acpi_mask)) {
pr_notice("asked for hotkey mask 0x%08x, but " pr_notice("asked for hotkey mask 0x%08x, but firmware forced it to 0x%08x\n",
"firmware forced it to 0x%08x\n",
fwmask, hotkey_acpi_mask); fwmask, hotkey_acpi_mask);
} }
...@@ -2192,11 +2189,9 @@ static int hotkey_user_mask_set(const u32 mask) ...@@ -2192,11 +2189,9 @@ static int hotkey_user_mask_set(const u32 mask)
(mask == 0xffff || mask == 0xffffff || (mask == 0xffff || mask == 0xffffff ||
mask == 0xffffffff)) { mask == 0xffffffff)) {
tp_warned.hotkey_mask_ff = 1; tp_warned.hotkey_mask_ff = 1;
pr_notice("setting the hotkey mask to 0x%08x is likely " pr_notice("setting the hotkey mask to 0x%08x is likely not the best way to go about it\n",
"not the best way to go about it\n", mask); mask);
pr_notice("please consider using the driver defaults, " pr_notice("please consider using the driver defaults, and refer to up-to-date thinkpad-acpi documentation\n");
"and refer to up-to-date thinkpad-acpi "
"documentation\n");
} }
/* Try to enable what the user asked for, plus whatever we need. /* Try to enable what the user asked for, plus whatever we need.
...@@ -2571,17 +2566,14 @@ static void hotkey_poll_setup(const bool may_warn) ...@@ -2571,17 +2566,14 @@ static void hotkey_poll_setup(const bool may_warn)
NULL, TPACPI_NVRAM_KTHREAD_NAME); NULL, TPACPI_NVRAM_KTHREAD_NAME);
if (IS_ERR(tpacpi_hotkey_task)) { if (IS_ERR(tpacpi_hotkey_task)) {
tpacpi_hotkey_task = NULL; tpacpi_hotkey_task = NULL;
pr_err("could not create kernel thread " pr_err("could not create kernel thread for hotkey polling\n");
"for hotkey polling\n");
} }
} }
} else { } else {
hotkey_poll_stop_sync(); hotkey_poll_stop_sync();
if (may_warn && (poll_driver_mask || poll_user_mask) && if (may_warn && (poll_driver_mask || poll_user_mask) &&
hotkey_poll_freq == 0) { hotkey_poll_freq == 0) {
pr_notice("hot keys 0x%08x and/or events 0x%08x " pr_notice("hot keys 0x%08x and/or events 0x%08x require polling, which is currently disabled\n",
"require polling, which is currently "
"disabled\n",
poll_user_mask, poll_driver_mask); poll_user_mask, poll_driver_mask);
} }
} }
...@@ -2808,12 +2800,10 @@ static ssize_t hotkey_source_mask_store(struct device *dev, ...@@ -2808,12 +2800,10 @@ static ssize_t hotkey_source_mask_store(struct device *dev,
mutex_unlock(&hotkey_mutex); mutex_unlock(&hotkey_mutex);
if (rc < 0) if (rc < 0)
pr_err("hotkey_source_mask: " pr_err("hotkey_source_mask: failed to update the firmware event mask!\n");
"failed to update the firmware event mask!\n");
if (r_ev) if (r_ev)
pr_notice("hotkey_source_mask: " pr_notice("hotkey_source_mask: some important events were disabled: 0x%04x\n",
"some important events were disabled: 0x%04x\n",
r_ev); r_ev);
tpacpi_disclose_usertask("hotkey_source_mask", "set to 0x%08lx\n", t); tpacpi_disclose_usertask("hotkey_source_mask", "set to 0x%08lx\n", t);
...@@ -3074,8 +3064,7 @@ static void hotkey_exit(void) ...@@ -3074,8 +3064,7 @@ static void hotkey_exit(void)
if (((tp_features.hotkey_mask && if (((tp_features.hotkey_mask &&
hotkey_mask_set(hotkey_orig_mask)) | hotkey_mask_set(hotkey_orig_mask)) |
hotkey_status_set(false)) != 0) hotkey_status_set(false)) != 0)
pr_err("failed to restore hot key mask " pr_err("failed to restore hot key mask to BIOS defaults\n");
"to BIOS defaults\n");
} }
static void __init hotkey_unmap(const unsigned int scancode) static void __init hotkey_unmap(const unsigned int scancode)
...@@ -3587,11 +3576,8 @@ static int __init hotkey_init(struct ibm_init_struct *iibm) ...@@ -3587,11 +3576,8 @@ static int __init hotkey_init(struct ibm_init_struct *iibm)
* userspace. tpacpi_detect_brightness_capabilities() must have * userspace. tpacpi_detect_brightness_capabilities() must have
* been called before this point */ * been called before this point */
if (acpi_video_get_backlight_type() != acpi_backlight_vendor) { if (acpi_video_get_backlight_type() != acpi_backlight_vendor) {
pr_info("This ThinkPad has standard ACPI backlight " pr_info("This ThinkPad has standard ACPI backlight brightness control, supported by the ACPI video driver\n");
"brightness control, supported by the ACPI " pr_notice("Disabling thinkpad-acpi brightness events by default...\n");
"video driver\n");
pr_notice("Disabling thinkpad-acpi brightness events "
"by default...\n");
/* Disable brightness up/down on Lenovo thinkpads when /* Disable brightness up/down on Lenovo thinkpads when
* ACPI is handling them, otherwise it is plain impossible * ACPI is handling them, otherwise it is plain impossible
...@@ -3760,7 +3746,7 @@ static bool adaptive_keyboard_hotkey_notify_hotkey(unsigned int scancode) ...@@ -3760,7 +3746,7 @@ static bool adaptive_keyboard_hotkey_notify_hotkey(unsigned int scancode)
TP_ACPI_HOTKEYSCAN_EXTENDED_START - TP_ACPI_HOTKEYSCAN_EXTENDED_START -
TP_ACPI_HOTKEYSCAN_ADAPTIVE_START) { TP_ACPI_HOTKEYSCAN_ADAPTIVE_START) {
pr_info("Unhandled adaptive keyboard key: 0x%x\n", pr_info("Unhandled adaptive keyboard key: 0x%x\n",
scancode); scancode);
return false; return false;
} }
keycode = hotkey_keycode_map[scancode - FIRST_ADAPTIVE_KEY + keycode = hotkey_keycode_map[scancode - FIRST_ADAPTIVE_KEY +
...@@ -3957,14 +3943,12 @@ static bool hotkey_notify_6xxx(const u32 hkey, ...@@ -3957,14 +3943,12 @@ static bool hotkey_notify_6xxx(const u32 hkey,
/* recommended action: immediate sleep/hibernate */ /* recommended action: immediate sleep/hibernate */
break; break;
case TP_HKEY_EV_ALARM_SENSOR_HOT: case TP_HKEY_EV_ALARM_SENSOR_HOT:
pr_crit("THERMAL ALARM: " pr_crit("THERMAL ALARM: a sensor reports something is too hot!\n");
"a sensor reports something is too hot!\n");
/* recommended action: warn user through gui, that */ /* recommended action: warn user through gui, that */
/* some internal component is too hot */ /* some internal component is too hot */
break; break;
case TP_HKEY_EV_ALARM_SENSOR_XHOT: case TP_HKEY_EV_ALARM_SENSOR_XHOT:
pr_alert("THERMAL EMERGENCY: " pr_alert("THERMAL EMERGENCY: a sensor reports something is extremely hot!\n");
"a sensor reports something is extremely hot!\n");
/* recommended action: immediate sleep/hibernate */ /* recommended action: immediate sleep/hibernate */
break; break;
case TP_HKEY_EV_AC_CHANGED: case TP_HKEY_EV_AC_CHANGED:
...@@ -4089,8 +4073,8 @@ static void hotkey_notify(struct ibm_struct *ibm, u32 event) ...@@ -4089,8 +4073,8 @@ static void hotkey_notify(struct ibm_struct *ibm, u32 event)
} }
if (!known_ev) { if (!known_ev) {
pr_notice("unhandled HKEY event 0x%04x\n", hkey); pr_notice("unhandled HKEY event 0x%04x\n", hkey);
pr_notice("please report the conditions when this " pr_notice("please report the conditions when this event happened to %s\n",
"event happened to %s\n", TPACPI_MAIL); TPACPI_MAIL);
} }
/* netlink events */ /* netlink events */
...@@ -4124,8 +4108,7 @@ static void hotkey_resume(void) ...@@ -4124,8 +4108,7 @@ static void hotkey_resume(void)
if (hotkey_status_set(true) < 0 || if (hotkey_status_set(true) < 0 ||
hotkey_mask_set(hotkey_acpi_mask) < 0) hotkey_mask_set(hotkey_acpi_mask) < 0)
pr_err("error while attempting to reset the event " pr_err("error while attempting to reset the event firmware interface\n");
"firmware interface\n");
tpacpi_send_radiosw_update(); tpacpi_send_radiosw_update();
hotkey_tablet_mode_notify_change(); hotkey_tablet_mode_notify_change();
...@@ -4177,12 +4160,8 @@ static void hotkey_enabledisable_warn(bool enable) ...@@ -4177,12 +4160,8 @@ static void hotkey_enabledisable_warn(bool enable)
{ {
tpacpi_log_usertask("procfs hotkey enable/disable"); tpacpi_log_usertask("procfs hotkey enable/disable");
if (!WARN((tpacpi_lifecycle == TPACPI_LIFE_RUNNING || !enable), if (!WARN((tpacpi_lifecycle == TPACPI_LIFE_RUNNING || !enable),
pr_fmt("hotkey enable/disable functionality has been " pr_fmt("hotkey enable/disable functionality has been removed from the driver. Hotkeys are always enabled.\n")))
"removed from the driver. " pr_err("Please remove the hotkey=enable module parameter, it is deprecated. Hotkeys are always enabled.\n");
"Hotkeys are always enabled.\n")))
pr_err("Please remove the hotkey=enable module "
"parameter, it is deprecated. "
"Hotkeys are always enabled.\n");
} }
static int hotkey_write(char *buf) static int hotkey_write(char *buf)
...@@ -4840,8 +4819,7 @@ static void video_exit(void) ...@@ -4840,8 +4819,7 @@ static void video_exit(void)
dbg_printk(TPACPI_DBG_EXIT, dbg_printk(TPACPI_DBG_EXIT,
"restoring original video autoswitch mode\n"); "restoring original video autoswitch mode\n");
if (video_autosw_set(video_orig_autosw)) if (video_autosw_set(video_orig_autosw))
pr_err("error while trying to restore original " pr_err("error while trying to restore original video autoswitch mode\n");
"video autoswitch mode\n");
} }
static int video_outputsw_get(void) static int video_outputsw_get(void)
...@@ -5931,8 +5909,7 @@ static int __init led_init(struct ibm_init_struct *iibm) ...@@ -5931,8 +5909,7 @@ static int __init led_init(struct ibm_init_struct *iibm)
} }
#ifdef CONFIG_THINKPAD_ACPI_UNSAFE_LEDS #ifdef CONFIG_THINKPAD_ACPI_UNSAFE_LEDS
pr_notice("warning: userspace override of important " pr_notice("warning: userspace override of important firmware LEDs is enabled\n");
"firmware LEDs is enabled\n");
#endif #endif
return 0; return 0;
} }
...@@ -5961,8 +5938,7 @@ static int led_read(struct seq_file *m) ...@@ -5961,8 +5938,7 @@ static int led_read(struct seq_file *m)
} }
} }
seq_printf(m, "commands:\t" seq_printf(m, "commands:\t<led> on, <led> off, <led> blink (<led> is 0-15)\n");
"<led> on, <led> off, <led> blink (<led> is 0-15)\n");
return 0; return 0;
} }
...@@ -6335,13 +6311,10 @@ static int __init thermal_init(struct ibm_init_struct *iibm) ...@@ -6335,13 +6311,10 @@ static int __init thermal_init(struct ibm_init_struct *iibm)
if (ta1 == 0) { if (ta1 == 0) {
/* This is sheer paranoia, but we handle it anyway */ /* This is sheer paranoia, but we handle it anyway */
if (acpi_tmp7) { if (acpi_tmp7) {
pr_err("ThinkPad ACPI EC access misbehaving, " pr_err("ThinkPad ACPI EC access misbehaving, falling back to ACPI TMPx access mode\n");
"falling back to ACPI TMPx access "
"mode\n");
thermal_read_mode = TPACPI_THERMAL_ACPI_TMP07; thermal_read_mode = TPACPI_THERMAL_ACPI_TMP07;
} else { } else {
pr_err("ThinkPad ACPI EC access misbehaving, " pr_err("ThinkPad ACPI EC access misbehaving, disabling thermal sensors access\n");
"disabling thermal sensors access\n");
thermal_read_mode = TPACPI_THERMAL_NONE; thermal_read_mode = TPACPI_THERMAL_NONE;
} }
} else { } else {
...@@ -6820,26 +6793,20 @@ static int __init brightness_init(struct ibm_init_struct *iibm) ...@@ -6820,26 +6793,20 @@ static int __init brightness_init(struct ibm_init_struct *iibm)
if (!brightness_enable) { if (!brightness_enable) {
dbg_printk(TPACPI_DBG_INIT | TPACPI_DBG_BRGHT, dbg_printk(TPACPI_DBG_INIT | TPACPI_DBG_BRGHT,
"brightness support disabled by " "brightness support disabled by module parameter\n");
"module parameter\n");
return 1; return 1;
} }
if (acpi_video_get_backlight_type() != acpi_backlight_vendor) { if (acpi_video_get_backlight_type() != acpi_backlight_vendor) {
if (brightness_enable > 1) { if (brightness_enable > 1) {
pr_info("Standard ACPI backlight interface " pr_info("Standard ACPI backlight interface available, not loading native one\n");
"available, not loading native one\n");
return 1; return 1;
} else if (brightness_enable == 1) { } else if (brightness_enable == 1) {
pr_warn("Cannot enable backlight brightness support, " pr_warn("Cannot enable backlight brightness support, ACPI is already handling it. Refer to the acpi_backlight kernel parameter.\n");
"ACPI is already handling it. Refer to the "
"acpi_backlight kernel parameter.\n");
return 1; return 1;
} }
} else if (tp_features.bright_acpimode && brightness_enable > 1) { } else if (tp_features.bright_acpimode && brightness_enable > 1) {
pr_notice("Standard ACPI backlight interface not " pr_notice("Standard ACPI backlight interface not available, thinkpad_acpi native brightness control enabled\n");
"available, thinkpad_acpi native "
"brightness control enabled\n");
} }
/* /*
...@@ -6890,10 +6857,10 @@ static int __init brightness_init(struct ibm_init_struct *iibm) ...@@ -6890,10 +6857,10 @@ static int __init brightness_init(struct ibm_init_struct *iibm)
"brightness is supported\n"); "brightness is supported\n");
if (quirks & TPACPI_BRGHT_Q_ASK) { if (quirks & TPACPI_BRGHT_Q_ASK) {
pr_notice("brightness: will use unverified default: " pr_notice("brightness: will use unverified default: brightness_mode=%d\n",
"brightness_mode=%d\n", brightness_mode); brightness_mode);
pr_notice("brightness: please report to %s whether it works well " pr_notice("brightness: please report to %s whether it works well or not on your ThinkPad\n",
"or not on your ThinkPad\n", TPACPI_MAIL); TPACPI_MAIL);
} }
/* Added by mistake in early 2007. Probably useless, but it could /* Added by mistake in early 2007. Probably useless, but it could
...@@ -6903,8 +6870,7 @@ static int __init brightness_init(struct ibm_init_struct *iibm) ...@@ -6903,8 +6870,7 @@ static int __init brightness_init(struct ibm_init_struct *iibm)
backlight_update_status(ibm_backlight_device); backlight_update_status(ibm_backlight_device);
vdbg_printk(TPACPI_DBG_INIT | TPACPI_DBG_BRGHT, vdbg_printk(TPACPI_DBG_INIT | TPACPI_DBG_BRGHT,
"brightness: registering brightness hotkeys " "brightness: registering brightness hotkeys as change notification\n");
"as change notification\n");
tpacpi_hotkey_driver_mask_set(hotkey_driver_mask tpacpi_hotkey_driver_mask_set(hotkey_driver_mask
| TP_ACPI_HKEY_BRGHTUP_MASK | TP_ACPI_HKEY_BRGHTUP_MASK
| TP_ACPI_HKEY_BRGHTDWN_MASK); | TP_ACPI_HKEY_BRGHTDWN_MASK);
...@@ -7567,8 +7533,8 @@ static int __init volume_init(struct ibm_init_struct *iibm) ...@@ -7567,8 +7533,8 @@ static int __init volume_init(struct ibm_init_struct *iibm)
return -EINVAL; return -EINVAL;
if (volume_mode == TPACPI_VOL_MODE_UCMS_STEP) { if (volume_mode == TPACPI_VOL_MODE_UCMS_STEP) {
pr_err("UCMS step volume mode not implemented, " pr_err("UCMS step volume mode not implemented, please contact %s\n",
"please contact %s\n", TPACPI_MAIL); TPACPI_MAIL);
return 1; return 1;
} }
...@@ -7581,8 +7547,7 @@ static int __init volume_init(struct ibm_init_struct *iibm) ...@@ -7581,8 +7547,7 @@ static int __init volume_init(struct ibm_init_struct *iibm)
*/ */
if (!alsa_enable) { if (!alsa_enable) {
vdbg_printk(TPACPI_DBG_INIT | TPACPI_DBG_MIXER, vdbg_printk(TPACPI_DBG_INIT | TPACPI_DBG_MIXER,
"ALSA mixer disabled by parameter, " "ALSA mixer disabled by parameter, not loading volume subdriver...\n");
"not loading volume subdriver...\n");
return 1; return 1;
} }
...@@ -7674,12 +7639,9 @@ static int volume_read(struct seq_file *m) ...@@ -7674,12 +7639,9 @@ static int volume_read(struct seq_file *m)
if (volume_control_allowed) { if (volume_control_allowed) {
seq_printf(m, "commands:\tunmute, mute\n"); seq_printf(m, "commands:\tunmute, mute\n");
if (!tp_features.mixer_no_level_control) { if (!tp_features.mixer_no_level_control) {
seq_printf(m, seq_printf(m, "commands:\tup, down\n");
"commands:\tup, down\n"); seq_printf(m, "commands:\tlevel <level> (<level> is 0-%d)\n",
seq_printf(m, TP_EC_VOLUME_MAX);
"commands:\tlevel <level>"
" (<level> is 0-%d)\n",
TP_EC_VOLUME_MAX);
} }
} }
} }
...@@ -7702,10 +7664,8 @@ static int volume_write(char *buf) ...@@ -7702,10 +7664,8 @@ static int volume_write(char *buf)
if (!volume_control_allowed && tpacpi_lifecycle != TPACPI_LIFE_INIT) { if (!volume_control_allowed && tpacpi_lifecycle != TPACPI_LIFE_INIT) {
if (unlikely(!tp_warned.volume_ctrl_forbidden)) { if (unlikely(!tp_warned.volume_ctrl_forbidden)) {
tp_warned.volume_ctrl_forbidden = 1; tp_warned.volume_ctrl_forbidden = 1;
pr_notice("Console audio control in monitor mode, " pr_notice("Console audio control in monitor mode, changes are not allowed\n");
"changes are not allowed\n"); pr_notice("Use the volume_control=1 module parameter to enable volume control\n");
pr_notice("Use the volume_control=1 module parameter "
"to enable volume control\n");
} }
return -EPERM; return -EPERM;
} }
...@@ -7987,8 +7947,7 @@ TPACPI_HANDLE(sfan, ec, "SFAN", /* 570 */ ...@@ -7987,8 +7947,7 @@ TPACPI_HANDLE(sfan, ec, "SFAN", /* 570 */
static void fan_quirk1_setup(void) static void fan_quirk1_setup(void)
{ {
if (fan_control_initial_status == 0x07) { if (fan_control_initial_status == 0x07) {
pr_notice("fan_init: initial fan status is unknown, " pr_notice("fan_init: initial fan status is unknown, assuming it is in auto mode\n");
"assuming it is in auto mode\n");
tp_features.fan_ctrl_status_undef = 1; tp_features.fan_ctrl_status_undef = 1;
} }
} }
...@@ -8385,8 +8344,8 @@ static void fan_watchdog_fire(struct work_struct *ignored) ...@@ -8385,8 +8344,8 @@ static void fan_watchdog_fire(struct work_struct *ignored)
pr_notice("fan watchdog: enabling fan\n"); pr_notice("fan watchdog: enabling fan\n");
rc = fan_set_enable(); rc = fan_set_enable();
if (rc < 0) { if (rc < 0) {
pr_err("fan watchdog: error %d while enabling fan, " pr_err("fan watchdog: error %d while enabling fan, will try again later...\n",
"will try again later...\n", -rc); rc);
/* reschedule for later */ /* reschedule for later */
fan_watchdog_reset(); fan_watchdog_reset();
} }
...@@ -8680,8 +8639,7 @@ static int __init fan_init(struct ibm_init_struct *iibm) ...@@ -8680,8 +8639,7 @@ static int __init fan_init(struct ibm_init_struct *iibm)
"secondary fan support enabled\n"); "secondary fan support enabled\n");
} }
} else { } else {
pr_err("ThinkPad ACPI EC access misbehaving, " pr_err("ThinkPad ACPI EC access misbehaving, fan status and control unavailable\n");
"fan status and control unavailable\n");
return 1; return 1;
} }
} }
...@@ -8780,8 +8738,8 @@ static void fan_suspend(void) ...@@ -8780,8 +8738,8 @@ static void fan_suspend(void)
fan_control_resume_level = 0; fan_control_resume_level = 0;
rc = fan_get_status_safe(&fan_control_resume_level); rc = fan_get_status_safe(&fan_control_resume_level);
if (rc < 0) if (rc < 0)
pr_notice("failed to read fan level for later " pr_notice("failed to read fan level for later restore during resume: %d\n",
"restore during resume: %d\n", rc); rc);
/* if it is undefined, don't attempt to restore it. /* if it is undefined, don't attempt to restore it.
* KEEP THIS LAST */ * KEEP THIS LAST */
...@@ -8900,20 +8858,17 @@ static int fan_read(struct seq_file *m) ...@@ -8900,20 +8858,17 @@ static int fan_read(struct seq_file *m)
break; break;
default: default:
seq_printf(m, " (<level> is 0-7, " seq_printf(m, " (<level> is 0-7, auto, disengaged, full-speed)\n");
"auto, disengaged, full-speed)\n");
break; break;
} }
} }
if (fan_control_commands & TPACPI_FAN_CMD_ENABLE) if (fan_control_commands & TPACPI_FAN_CMD_ENABLE)
seq_printf(m, "commands:\tenable, disable\n" seq_printf(m, "commands:\tenable, disable\n"
"commands:\twatchdog <timeout> (<timeout> " "commands:\twatchdog <timeout> (<timeout> is 0 (off), 1-120 (seconds))\n");
"is 0 (off), 1-120 (seconds))\n");
if (fan_control_commands & TPACPI_FAN_CMD_SPEED) if (fan_control_commands & TPACPI_FAN_CMD_SPEED)
seq_printf(m, "commands:\tspeed <speed>" seq_printf(m, "commands:\tspeed <speed> (<speed> is 0-65535)\n");
" (<speed> is 0-65535)\n");
return 0; return 0;
} }
...@@ -9439,8 +9394,7 @@ static int __must_check __init get_thinkpad_model_data( ...@@ -9439,8 +9394,7 @@ static int __must_check __init get_thinkpad_model_data(
tp->ec_release = (ec_fw_string[4] << 8) tp->ec_release = (ec_fw_string[4] << 8)
| ec_fw_string[5]; | ec_fw_string[5];
} else { } else {
pr_notice("ThinkPad firmware release %s " pr_notice("ThinkPad firmware release %s doesn't match the known patterns\n",
"doesn't match the known patterns\n",
ec_fw_string); ec_fw_string);
pr_notice("please report this to %s\n", pr_notice("please report this to %s\n",
TPACPI_MAIL); TPACPI_MAIL);
...@@ -9635,8 +9589,7 @@ MODULE_PARM_DESC(debug, "Sets debug level bit-mask"); ...@@ -9635,8 +9589,7 @@ MODULE_PARM_DESC(debug, "Sets debug level bit-mask");
module_param(force_load, bool, 0444); module_param(force_load, bool, 0444);
MODULE_PARM_DESC(force_load, MODULE_PARM_DESC(force_load,
"Attempts to load the driver even on a " "Attempts to load the driver even on a mis-identified ThinkPad when true");
"mis-identified ThinkPad when true");
module_param_named(fan_control, fan_control_allowed, bool, 0444); module_param_named(fan_control, fan_control_allowed, bool, 0444);
MODULE_PARM_DESC(fan_control, MODULE_PARM_DESC(fan_control,
...@@ -9644,8 +9597,7 @@ MODULE_PARM_DESC(fan_control, ...@@ -9644,8 +9597,7 @@ MODULE_PARM_DESC(fan_control,
module_param_named(brightness_mode, brightness_mode, uint, 0444); module_param_named(brightness_mode, brightness_mode, uint, 0444);
MODULE_PARM_DESC(brightness_mode, MODULE_PARM_DESC(brightness_mode,
"Selects brightness control strategy: " "Selects brightness control strategy: 0=auto, 1=EC, 2=UCMS, 3=EC+NVRAM");
"0=auto, 1=EC, 2=UCMS, 3=EC+NVRAM");
module_param(brightness_enable, uint, 0444); module_param(brightness_enable, uint, 0444);
MODULE_PARM_DESC(brightness_enable, MODULE_PARM_DESC(brightness_enable,
...@@ -9654,18 +9606,15 @@ MODULE_PARM_DESC(brightness_enable, ...@@ -9654,18 +9606,15 @@ MODULE_PARM_DESC(brightness_enable,
#ifdef CONFIG_THINKPAD_ACPI_ALSA_SUPPORT #ifdef CONFIG_THINKPAD_ACPI_ALSA_SUPPORT
module_param_named(volume_mode, volume_mode, uint, 0444); module_param_named(volume_mode, volume_mode, uint, 0444);
MODULE_PARM_DESC(volume_mode, MODULE_PARM_DESC(volume_mode,
"Selects volume control strategy: " "Selects volume control strategy: 0=auto, 1=EC, 2=N/A, 3=EC+NVRAM");
"0=auto, 1=EC, 2=N/A, 3=EC+NVRAM");
module_param_named(volume_capabilities, volume_capabilities, uint, 0444); module_param_named(volume_capabilities, volume_capabilities, uint, 0444);
MODULE_PARM_DESC(volume_capabilities, MODULE_PARM_DESC(volume_capabilities,
"Selects the mixer capabilites: " "Selects the mixer capabilites: 0=auto, 1=volume and mute, 2=mute only");
"0=auto, 1=volume and mute, 2=mute only");
module_param_named(volume_control, volume_control_allowed, bool, 0444); module_param_named(volume_control, volume_control_allowed, bool, 0444);
MODULE_PARM_DESC(volume_control, MODULE_PARM_DESC(volume_control,
"Enables software override for the console audio " "Enables software override for the console audio control when true");
"control when true");
module_param_named(software_mute, software_mute_requested, bool, 0444); module_param_named(software_mute, software_mute_requested, bool, 0444);
MODULE_PARM_DESC(software_mute, MODULE_PARM_DESC(software_mute,
...@@ -9680,10 +9629,10 @@ module_param_named(enable, alsa_enable, bool, 0444); ...@@ -9680,10 +9629,10 @@ module_param_named(enable, alsa_enable, bool, 0444);
MODULE_PARM_DESC(enable, "Enable the ALSA interface for the ACPI EC Mixer"); MODULE_PARM_DESC(enable, "Enable the ALSA interface for the ACPI EC Mixer");
#endif /* CONFIG_THINKPAD_ACPI_ALSA_SUPPORT */ #endif /* CONFIG_THINKPAD_ACPI_ALSA_SUPPORT */
/* The module parameter can't be read back, that's why 0 is used here */
#define TPACPI_PARAM(feature) \ #define TPACPI_PARAM(feature) \
module_param_call(feature, set_ibm_param, NULL, NULL, 0); \ module_param_call(feature, set_ibm_param, NULL, NULL, 0); \
MODULE_PARM_DESC(feature, "Simulates thinkpad-acpi procfs command " \ MODULE_PARM_DESC(feature, "Simulates thinkpad-acpi procfs command at module load, see documentation")
"at module load, see documentation")
TPACPI_PARAM(hotkey); TPACPI_PARAM(hotkey);
TPACPI_PARAM(bluetooth); TPACPI_PARAM(bluetooth);
......
...@@ -162,6 +162,7 @@ static int acpi_topstar_remove(struct acpi_device *device) ...@@ -162,6 +162,7 @@ static int acpi_topstar_remove(struct acpi_device *device)
} }
static const struct acpi_device_id topstar_device_ids[] = { static const struct acpi_device_id topstar_device_ids[] = {
{ "TPS0001", 0 },
{ "TPSACPI01", 0 }, { "TPSACPI01", 0 },
{ "", 0 }, { "", 0 },
}; };
......
...@@ -1502,14 +1502,9 @@ static ssize_t video_proc_write(struct file *file, const char __user *buf, ...@@ -1502,14 +1502,9 @@ static ssize_t video_proc_write(struct file *file, const char __user *buf,
int ret; int ret;
u32 video_out; u32 video_out;
cmd = kmalloc(count + 1, GFP_KERNEL); cmd = memdup_user_nul(buf, count);
if (!cmd) if (IS_ERR(cmd))
return -ENOMEM; return PTR_ERR(cmd);
if (copy_from_user(cmd, buf, count)) {
kfree(cmd);
return -EFAULT;
}
cmd[count] = '\0';
buffer = cmd; buffer = cmd;
......
...@@ -132,7 +132,7 @@ static struct attribute *haps_attributes[] = { ...@@ -132,7 +132,7 @@ static struct attribute *haps_attributes[] = {
NULL, NULL,
}; };
static struct attribute_group haps_attr_group = { static const struct attribute_group haps_attr_group = {
.attrs = haps_attributes, .attrs = haps_attributes,
}; };
......
/*
* WMI embedded Binary MOF driver
*
* Copyright (c) 2015 Andrew Lutomirski
* Copyright (C) 2017 VMware, 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 WMI_BMOF_GUID "05901221-D566-11D1-B2F0-00A0C9062910"
struct bmof_priv {
union acpi_object *bmofdata;
struct bin_attribute bmof_bin_attr;
};
static ssize_t
read_bmof(struct file *filp, struct kobject *kobj,
struct bin_attribute *attr,
char *buf, loff_t off, size_t count)
{
struct bmof_priv *priv =
container_of(attr, struct bmof_priv, bmof_bin_attr);
if (off < 0)
return -EINVAL;
if (off >= priv->bmofdata->buffer.length)
return 0;
if (count > priv->bmofdata->buffer.length - off)
count = priv->bmofdata->buffer.length - off;
memcpy(buf, priv->bmofdata->buffer.pointer + off, count);
return count;
}
static int wmi_bmof_probe(struct wmi_device *wdev)
{
struct bmof_priv *priv;
int ret;
priv = devm_kzalloc(&wdev->dev, sizeof(struct bmof_priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
dev_set_drvdata(&wdev->dev, priv);
priv->bmofdata = wmidev_block_query(wdev, 0);
if (!priv->bmofdata) {
dev_err(&wdev->dev, "failed to read Binary MOF\n");
return -EIO;
}
if (priv->bmofdata->type != ACPI_TYPE_BUFFER) {
dev_err(&wdev->dev, "Binary MOF is not a buffer\n");
ret = -EIO;
goto err_free;
}
sysfs_bin_attr_init(&priv->bmof_bin_attr);
priv->bmof_bin_attr.attr.name = "bmof";
priv->bmof_bin_attr.attr.mode = 0400;
priv->bmof_bin_attr.read = read_bmof;
priv->bmof_bin_attr.size = priv->bmofdata->buffer.length;
ret = sysfs_create_bin_file(&wdev->dev.kobj, &priv->bmof_bin_attr);
if (ret)
goto err_free;
return 0;
err_free:
kfree(priv->bmofdata);
return ret;
}
static int wmi_bmof_remove(struct wmi_device *wdev)
{
struct bmof_priv *priv = dev_get_drvdata(&wdev->dev);
sysfs_remove_bin_file(&wdev->dev.kobj, &priv->bmof_bin_attr);
kfree(priv->bmofdata);
return 0;
}
static const struct wmi_device_id wmi_bmof_id_table[] = {
{ .guid_string = WMI_BMOF_GUID },
{ },
};
static struct wmi_driver wmi_bmof_driver = {
.driver = {
.name = "wmi-bmof",
},
.probe = wmi_bmof_probe,
.remove = wmi_bmof_remove,
.id_table = wmi_bmof_id_table,
};
module_wmi_driver(wmi_bmof_driver);
MODULE_ALIAS("wmi:" WMI_BMOF_GUID);
MODULE_AUTHOR("Andrew Lutomirski <luto@kernel.org>");
MODULE_DESCRIPTION("WMI embedded Binary MOF driver");
MODULE_LICENSE("GPL");
...@@ -8,6 +8,10 @@ ...@@ -8,6 +8,10 @@
* Copyright (c) 2001-2007 Anton Altaparmakov * Copyright (c) 2001-2007 Anton Altaparmakov
* Copyright (C) 2001,2002 Jakob Kemi <jakob.kemi@telia.com> * Copyright (C) 2001,2002 Jakob Kemi <jakob.kemi@telia.com>
* *
* WMI bus infrastructure by Andrew Lutomirski and Darren Hart:
* Copyright (C) 2015 Andrew Lutomirski
* Copyright (C) 2017 VMware, Inc. All Rights Reserved.
*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* *
* This program is free software; you can redistribute it and/or modify * This program is free software; you can redistribute it and/or modify
...@@ -37,6 +41,8 @@ ...@@ -37,6 +41,8 @@
#include <linux/acpi.h> #include <linux/acpi.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/wmi.h>
#include <linux/uuid.h> #include <linux/uuid.h>
ACPI_MODULE_NAME("wmi"); ACPI_MODULE_NAME("wmi");
...@@ -44,8 +50,6 @@ MODULE_AUTHOR("Carlos Corbacho"); ...@@ -44,8 +50,6 @@ MODULE_AUTHOR("Carlos Corbacho");
MODULE_DESCRIPTION("ACPI-WMI Mapping Driver"); MODULE_DESCRIPTION("ACPI-WMI Mapping Driver");
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
#define ACPI_WMI_CLASS "wmi"
static LIST_HEAD(wmi_block_list); static LIST_HEAD(wmi_block_list);
struct guid_block { struct guid_block {
...@@ -62,12 +66,14 @@ struct guid_block { ...@@ -62,12 +66,14 @@ struct guid_block {
}; };
struct wmi_block { struct wmi_block {
struct wmi_device dev;
struct list_head list; struct list_head list;
struct guid_block gblock; struct guid_block gblock;
acpi_handle handle; struct acpi_device *acpi_device;
wmi_notify_handler handler; wmi_notify_handler handler;
void *handler_data; void *handler_data;
struct device dev;
bool read_takes_no_args;
}; };
...@@ -90,9 +96,8 @@ module_param(debug_dump_wdg, bool, 0444); ...@@ -90,9 +96,8 @@ module_param(debug_dump_wdg, bool, 0444);
MODULE_PARM_DESC(debug_dump_wdg, MODULE_PARM_DESC(debug_dump_wdg,
"Dump available WMI interfaces [0/1]"); "Dump available WMI interfaces [0/1]");
static int acpi_wmi_remove(struct acpi_device *device); static int acpi_wmi_remove(struct platform_device *device);
static int acpi_wmi_add(struct acpi_device *device); static int acpi_wmi_probe(struct platform_device *device);
static void acpi_wmi_notify(struct acpi_device *device, u32 event);
static const struct acpi_device_id wmi_device_ids[] = { static const struct acpi_device_id wmi_device_ids[] = {
{"PNP0C14", 0}, {"PNP0C14", 0},
...@@ -101,15 +106,13 @@ static const struct acpi_device_id wmi_device_ids[] = { ...@@ -101,15 +106,13 @@ static const struct acpi_device_id wmi_device_ids[] = {
}; };
MODULE_DEVICE_TABLE(acpi, wmi_device_ids); MODULE_DEVICE_TABLE(acpi, wmi_device_ids);
static struct acpi_driver acpi_wmi_driver = { static struct platform_driver acpi_wmi_driver = {
.name = "wmi", .driver = {
.class = ACPI_WMI_CLASS, .name = "acpi-wmi",
.ids = wmi_device_ids, .acpi_match_table = wmi_device_ids,
.ops = {
.add = acpi_wmi_add,
.remove = acpi_wmi_remove,
.notify = acpi_wmi_notify,
}, },
.probe = acpi_wmi_probe,
.remove = acpi_wmi_remove,
}; };
/* /*
...@@ -139,6 +142,30 @@ static bool find_guid(const char *guid_string, struct wmi_block **out) ...@@ -139,6 +142,30 @@ static bool find_guid(const char *guid_string, struct wmi_block **out)
return false; return false;
} }
static int get_subobj_info(acpi_handle handle, const char *pathname,
struct acpi_device_info **info)
{
struct acpi_device_info *dummy_info, **info_ptr;
acpi_handle subobj_handle;
acpi_status status;
status = acpi_get_handle(handle, (char *)pathname, &subobj_handle);
if (status == AE_NOT_FOUND)
return -ENOENT;
else if (ACPI_FAILURE(status))
return -EIO;
info_ptr = info ? info : &dummy_info;
status = acpi_get_object_info(subobj_handle, info_ptr);
if (ACPI_FAILURE(status))
return -EIO;
if (!info)
kfree(dummy_info);
return 0;
}
static acpi_status wmi_method_enable(struct wmi_block *wblock, int enable) static acpi_status wmi_method_enable(struct wmi_block *wblock, int enable)
{ {
struct guid_block *block = NULL; struct guid_block *block = NULL;
...@@ -147,7 +174,7 @@ static acpi_status wmi_method_enable(struct wmi_block *wblock, int enable) ...@@ -147,7 +174,7 @@ static acpi_status wmi_method_enable(struct wmi_block *wblock, int enable)
acpi_handle handle; acpi_handle handle;
block = &wblock->gblock; block = &wblock->gblock;
handle = wblock->handle; handle = wblock->acpi_device->handle;
snprintf(method, 5, "WE%02X", block->notify_id); snprintf(method, 5, "WE%02X", block->notify_id);
status = acpi_execute_simple_method(handle, method, enable); status = acpi_execute_simple_method(handle, method, enable);
...@@ -186,7 +213,7 @@ u32 method_id, const struct acpi_buffer *in, struct acpi_buffer *out) ...@@ -186,7 +213,7 @@ u32 method_id, const struct acpi_buffer *in, struct acpi_buffer *out)
return AE_ERROR; return AE_ERROR;
block = &wblock->gblock; block = &wblock->gblock;
handle = wblock->handle; handle = wblock->acpi_device->handle;
if (!(block->flags & ACPI_WMI_METHOD)) if (!(block->flags & ACPI_WMI_METHOD))
return AE_BAD_DATA; return AE_BAD_DATA;
...@@ -221,19 +248,10 @@ u32 method_id, const struct acpi_buffer *in, struct acpi_buffer *out) ...@@ -221,19 +248,10 @@ u32 method_id, const struct acpi_buffer *in, struct acpi_buffer *out)
} }
EXPORT_SYMBOL_GPL(wmi_evaluate_method); EXPORT_SYMBOL_GPL(wmi_evaluate_method);
/** static acpi_status __query_block(struct wmi_block *wblock, u8 instance,
* wmi_query_block - Return contents of a WMI block struct acpi_buffer *out)
* @guid_string: 36 char string of the form fa50ff2b-f2e8-45de-83fa-65417f2f49ba
* @instance: Instance index
* &out: Empty buffer to return the contents of the data block to
*
* Return the contents of an ACPI-WMI data block to a buffer
*/
acpi_status wmi_query_block(const char *guid_string, u8 instance,
struct acpi_buffer *out)
{ {
struct guid_block *block = NULL; struct guid_block *block = NULL;
struct wmi_block *wblock = NULL;
acpi_handle handle; acpi_handle handle;
acpi_status status, wc_status = AE_ERROR; acpi_status status, wc_status = AE_ERROR;
struct acpi_object_list input; struct acpi_object_list input;
...@@ -241,14 +259,11 @@ struct acpi_buffer *out) ...@@ -241,14 +259,11 @@ struct acpi_buffer *out)
char method[5]; char method[5];
char wc_method[5] = "WC"; char wc_method[5] = "WC";
if (!guid_string || !out) if (!out)
return AE_BAD_PARAMETER; return AE_BAD_PARAMETER;
if (!find_guid(guid_string, &wblock))
return AE_ERROR;
block = &wblock->gblock; block = &wblock->gblock;
handle = wblock->handle; handle = wblock->acpi_device->handle;
if (block->instance_count < instance) if (block->instance_count < instance)
return AE_BAD_PARAMETER; return AE_BAD_PARAMETER;
...@@ -262,6 +277,9 @@ struct acpi_buffer *out) ...@@ -262,6 +277,9 @@ struct acpi_buffer *out)
wq_params[0].type = ACPI_TYPE_INTEGER; wq_params[0].type = ACPI_TYPE_INTEGER;
wq_params[0].integer.value = instance; wq_params[0].integer.value = instance;
if (instance == 0 && wblock->read_takes_no_args)
input.count = 0;
/* /*
* If ACPI_WMI_EXPENSIVE, call the relevant WCxx method first to * If ACPI_WMI_EXPENSIVE, call the relevant WCxx method first to
* enable collection. * enable collection.
...@@ -294,8 +312,59 @@ struct acpi_buffer *out) ...@@ -294,8 +312,59 @@ struct acpi_buffer *out)
return status; return status;
} }
/**
* wmi_query_block - Return contents of a WMI block (deprecated)
* @guid_string: 36 char string of the form fa50ff2b-f2e8-45de-83fa-65417f2f49ba
* @instance: Instance index
* &out: Empty buffer to return the contents of the data block to
*
* Return the contents of an ACPI-WMI data block to a buffer
*/
acpi_status wmi_query_block(const char *guid_string, u8 instance,
struct acpi_buffer *out)
{
struct wmi_block *wblock;
if (!guid_string)
return AE_BAD_PARAMETER;
if (!find_guid(guid_string, &wblock))
return AE_ERROR;
return __query_block(wblock, instance, out);
}
EXPORT_SYMBOL_GPL(wmi_query_block); EXPORT_SYMBOL_GPL(wmi_query_block);
union acpi_object *wmidev_block_query(struct wmi_device *wdev, u8 instance)
{
struct acpi_buffer out = { ACPI_ALLOCATE_BUFFER, NULL };
struct wmi_block *wblock = container_of(wdev, struct wmi_block, dev);
if (ACPI_FAILURE(__query_block(wblock, instance, &out)))
return NULL;
return (union acpi_object *)out.pointer;
}
EXPORT_SYMBOL_GPL(wmidev_block_query);
struct wmi_device *wmidev_get_other_guid(struct wmi_device *wdev,
const char *guid_string)
{
struct wmi_block *this_wb = container_of(wdev, struct wmi_block, dev);
struct wmi_block *other_wb;
if (!find_guid(guid_string, &other_wb))
return NULL;
if (other_wb->acpi_device != this_wb->acpi_device)
return NULL;
get_device(&other_wb->dev.dev);
return &other_wb->dev;
}
EXPORT_SYMBOL_GPL(wmidev_get_other_guid);
/** /**
* wmi_set_block - Write to a WMI block * wmi_set_block - Write to a WMI block
* @guid_string: 36 char string of the form fa50ff2b-f2e8-45de-83fa-65417f2f49ba * @guid_string: 36 char string of the form fa50ff2b-f2e8-45de-83fa-65417f2f49ba
...@@ -305,7 +374,7 @@ EXPORT_SYMBOL_GPL(wmi_query_block); ...@@ -305,7 +374,7 @@ EXPORT_SYMBOL_GPL(wmi_query_block);
* Write the contents of the input buffer to an ACPI-WMI data block * Write the contents of the input buffer to an ACPI-WMI data block
*/ */
acpi_status wmi_set_block(const char *guid_string, u8 instance, acpi_status wmi_set_block(const char *guid_string, u8 instance,
const struct acpi_buffer *in) const struct acpi_buffer *in)
{ {
struct guid_block *block = NULL; struct guid_block *block = NULL;
struct wmi_block *wblock = NULL; struct wmi_block *wblock = NULL;
...@@ -321,7 +390,7 @@ const struct acpi_buffer *in) ...@@ -321,7 +390,7 @@ const struct acpi_buffer *in)
return AE_ERROR; return AE_ERROR;
block = &wblock->gblock; block = &wblock->gblock;
handle = wblock->handle; handle = wblock->acpi_device->handle;
if (block->instance_count < instance) if (block->instance_count < instance)
return AE_BAD_PARAMETER; return AE_BAD_PARAMETER;
...@@ -352,9 +421,10 @@ EXPORT_SYMBOL_GPL(wmi_set_block); ...@@ -352,9 +421,10 @@ EXPORT_SYMBOL_GPL(wmi_set_block);
static void wmi_dump_wdg(const struct guid_block *g) static void wmi_dump_wdg(const struct guid_block *g)
{ {
pr_info("%pUL:\n", g->guid); pr_info("%pUL:\n", g->guid);
pr_info("\tobject_id: %c%c\n", g->object_id[0], g->object_id[1]); if (g->flags & ACPI_WMI_EVENT)
pr_info("\tnotify_id: %02X\n", g->notify_id); pr_info("\tnotify_id: 0x%02X\n", g->notify_id);
pr_info("\treserved: %02X\n", g->reserved); else
pr_info("\tobject_id: %2pE\n", g->object_id);
pr_info("\tinstance_count: %d\n", g->instance_count); pr_info("\tinstance_count: %d\n", g->instance_count);
pr_info("\tflags: %#x", g->flags); pr_info("\tflags: %#x", g->flags);
if (g->flags) { if (g->flags) {
...@@ -525,8 +595,8 @@ acpi_status wmi_get_event_data(u32 event, struct acpi_buffer *out) ...@@ -525,8 +595,8 @@ acpi_status wmi_get_event_data(u32 event, struct acpi_buffer *out)
if ((gblock->flags & ACPI_WMI_EVENT) && if ((gblock->flags & ACPI_WMI_EVENT) &&
(gblock->notify_id == event)) (gblock->notify_id == event))
return acpi_evaluate_object(wblock->handle, "_WED", return acpi_evaluate_object(wblock->acpi_device->handle,
&input, out); "_WED", &input, out);
} }
return AE_NOT_FOUND; return AE_NOT_FOUND;
...@@ -545,99 +615,320 @@ bool wmi_has_guid(const char *guid_string) ...@@ -545,99 +615,320 @@ bool wmi_has_guid(const char *guid_string)
} }
EXPORT_SYMBOL_GPL(wmi_has_guid); EXPORT_SYMBOL_GPL(wmi_has_guid);
static struct wmi_block *dev_to_wblock(struct device *dev)
{
return container_of(dev, struct wmi_block, dev.dev);
}
static struct wmi_device *dev_to_wdev(struct device *dev)
{
return container_of(dev, struct wmi_device, dev);
}
/* /*
* sysfs interface * sysfs interface
*/ */
static ssize_t modalias_show(struct device *dev, struct device_attribute *attr, static ssize_t modalias_show(struct device *dev, struct device_attribute *attr,
char *buf) char *buf)
{ {
struct wmi_block *wblock; struct wmi_block *wblock = dev_to_wblock(dev);
wblock = dev_get_drvdata(dev);
if (!wblock) {
strcat(buf, "\n");
return strlen(buf);
}
return sprintf(buf, "wmi:%pUL\n", wblock->gblock.guid); return sprintf(buf, "wmi:%pUL\n", wblock->gblock.guid);
} }
static DEVICE_ATTR_RO(modalias); static DEVICE_ATTR_RO(modalias);
static ssize_t guid_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
struct wmi_block *wblock = dev_to_wblock(dev);
return sprintf(buf, "%pUL\n", wblock->gblock.guid);
}
static DEVICE_ATTR_RO(guid);
static ssize_t instance_count_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct wmi_block *wblock = dev_to_wblock(dev);
return sprintf(buf, "%d\n", (int)wblock->gblock.instance_count);
}
static DEVICE_ATTR_RO(instance_count);
static ssize_t expensive_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct wmi_block *wblock = dev_to_wblock(dev);
return sprintf(buf, "%d\n",
(wblock->gblock.flags & ACPI_WMI_EXPENSIVE) != 0);
}
static DEVICE_ATTR_RO(expensive);
static struct attribute *wmi_attrs[] = { static struct attribute *wmi_attrs[] = {
&dev_attr_modalias.attr, &dev_attr_modalias.attr,
&dev_attr_guid.attr,
&dev_attr_instance_count.attr,
&dev_attr_expensive.attr,
NULL, NULL,
}; };
ATTRIBUTE_GROUPS(wmi); ATTRIBUTE_GROUPS(wmi);
static int wmi_dev_uevent(struct device *dev, struct kobj_uevent_env *env) static ssize_t notify_id_show(struct device *dev, struct device_attribute *attr,
char *buf)
{ {
char guid_string[37]; struct wmi_block *wblock = dev_to_wblock(dev);
struct wmi_block *wblock; return sprintf(buf, "%02X\n", (unsigned int)wblock->gblock.notify_id);
}
static DEVICE_ATTR_RO(notify_id);
static struct attribute *wmi_event_attrs[] = {
&dev_attr_notify_id.attr,
NULL,
};
ATTRIBUTE_GROUPS(wmi_event);
static ssize_t object_id_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
struct wmi_block *wblock = dev_to_wblock(dev);
return sprintf(buf, "%c%c\n", wblock->gblock.object_id[0],
wblock->gblock.object_id[1]);
}
static DEVICE_ATTR_RO(object_id);
static ssize_t setable_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
struct wmi_device *wdev = dev_to_wdev(dev);
return sprintf(buf, "%d\n", (int)wdev->setable);
}
static DEVICE_ATTR_RO(setable);
if (add_uevent_var(env, "MODALIAS=")) static struct attribute *wmi_data_attrs[] = {
&dev_attr_object_id.attr,
&dev_attr_setable.attr,
NULL,
};
ATTRIBUTE_GROUPS(wmi_data);
static struct attribute *wmi_method_attrs[] = {
&dev_attr_object_id.attr,
NULL,
};
ATTRIBUTE_GROUPS(wmi_method);
static int wmi_dev_uevent(struct device *dev, struct kobj_uevent_env *env)
{
struct wmi_block *wblock = dev_to_wblock(dev);
if (add_uevent_var(env, "MODALIAS=wmi:%pUL", wblock->gblock.guid))
return -ENOMEM; return -ENOMEM;
wblock = dev_get_drvdata(dev); if (add_uevent_var(env, "WMI_GUID=%pUL", wblock->gblock.guid))
if (!wblock)
return -ENOMEM; return -ENOMEM;
sprintf(guid_string, "%pUL", wblock->gblock.guid); return 0;
}
strcpy(&env->buf[env->buflen - 1], "wmi:"); static void wmi_dev_release(struct device *dev)
memcpy(&env->buf[env->buflen - 1 + 4], guid_string, 36); {
env->buflen += 40; struct wmi_block *wblock = dev_to_wblock(dev);
kfree(wblock);
}
static int wmi_dev_match(struct device *dev, struct device_driver *driver)
{
struct wmi_driver *wmi_driver =
container_of(driver, struct wmi_driver, driver);
struct wmi_block *wblock = dev_to_wblock(dev);
const struct wmi_device_id *id = wmi_driver->id_table;
while (id->guid_string) {
uuid_le driver_guid;
if (WARN_ON(uuid_le_to_bin(id->guid_string, &driver_guid)))
continue;
if (!memcmp(&driver_guid, wblock->gblock.guid, 16))
return 1;
id++;
}
return 0; return 0;
} }
static void wmi_dev_free(struct device *dev) static int wmi_dev_probe(struct device *dev)
{ {
struct wmi_block *wmi_block = container_of(dev, struct wmi_block, dev); struct wmi_block *wblock = dev_to_wblock(dev);
struct wmi_driver *wdriver =
container_of(dev->driver, struct wmi_driver, driver);
int ret = 0;
if (ACPI_FAILURE(wmi_method_enable(wblock, 1)))
dev_warn(dev, "failed to enable device -- probing anyway\n");
if (wdriver->probe) {
ret = wdriver->probe(dev_to_wdev(dev));
if (ret != 0 && ACPI_FAILURE(wmi_method_enable(wblock, 0)))
dev_warn(dev, "failed to disable device\n");
}
return ret;
}
static int wmi_dev_remove(struct device *dev)
{
struct wmi_block *wblock = dev_to_wblock(dev);
struct wmi_driver *wdriver =
container_of(dev->driver, struct wmi_driver, driver);
int ret = 0;
if (wdriver->remove)
ret = wdriver->remove(dev_to_wdev(dev));
if (ACPI_FAILURE(wmi_method_enable(wblock, 0)))
dev_warn(dev, "failed to disable device\n");
kfree(wmi_block); return ret;
} }
static struct class wmi_class = { static struct class wmi_bus_class = {
.name = "wmi_bus",
};
static struct bus_type wmi_bus_type = {
.name = "wmi", .name = "wmi",
.dev_release = wmi_dev_free,
.dev_uevent = wmi_dev_uevent,
.dev_groups = wmi_groups, .dev_groups = wmi_groups,
.match = wmi_dev_match,
.uevent = wmi_dev_uevent,
.probe = wmi_dev_probe,
.remove = wmi_dev_remove,
};
static struct device_type wmi_type_event = {
.name = "event",
.groups = wmi_event_groups,
.release = wmi_dev_release,
}; };
static int wmi_create_device(const struct guid_block *gblock, static struct device_type wmi_type_method = {
struct wmi_block *wblock, acpi_handle handle) .name = "method",
.groups = wmi_method_groups,
.release = wmi_dev_release,
};
static struct device_type wmi_type_data = {
.name = "data",
.groups = wmi_data_groups,
.release = wmi_dev_release,
};
static int wmi_create_device(struct device *wmi_bus_dev,
const struct guid_block *gblock,
struct wmi_block *wblock,
struct acpi_device *device)
{ {
wblock->dev.class = &wmi_class; struct acpi_device_info *info;
char method[5];
int result;
dev_set_name(&wblock->dev, "%pUL", gblock->guid); if (gblock->flags & ACPI_WMI_EVENT) {
wblock->dev.dev.type = &wmi_type_event;
goto out_init;
}
dev_set_drvdata(&wblock->dev, wblock); if (gblock->flags & ACPI_WMI_METHOD) {
wblock->dev.dev.type = &wmi_type_method;
goto out_init;
}
return device_register(&wblock->dev); /*
* Data Block Query Control Method (WQxx by convention) is
* required per the WMI documentation. If it is not present,
* we ignore this data block.
*/
strcpy(method, "WQ");
strncat(method, wblock->gblock.object_id, 2);
result = get_subobj_info(device->handle, method, &info);
if (result) {
dev_warn(wmi_bus_dev,
"%s data block query control method not found",
method);
return result;
}
wblock->dev.dev.type = &wmi_type_data;
/*
* The Microsoft documentation specifically states:
*
* Data blocks registered with only a single instance
* can ignore the parameter.
*
* ACPICA will get mad at us if we call the method with the wrong number
* of arguments, so check what our method expects. (On some Dell
* laptops, WQxx may not be a method at all.)
*/
if (info->type != ACPI_TYPE_METHOD || info->param_count == 0)
wblock->read_takes_no_args = true;
kfree(info);
strcpy(method, "WS");
strncat(method, wblock->gblock.object_id, 2);
result = get_subobj_info(device->handle, method, NULL);
if (result == 0)
wblock->dev.setable = true;
out_init:
wblock->dev.dev.bus = &wmi_bus_type;
wblock->dev.dev.parent = wmi_bus_dev;
dev_set_name(&wblock->dev.dev, "%pUL", gblock->guid);
device_initialize(&wblock->dev.dev);
return 0;
} }
static void wmi_free_devices(void) static void wmi_free_devices(struct acpi_device *device)
{ {
struct wmi_block *wblock, *next; struct wmi_block *wblock, *next;
/* Delete devices for all the GUIDs */ /* Delete devices for all the GUIDs */
list_for_each_entry_safe(wblock, next, &wmi_block_list, list) { list_for_each_entry_safe(wblock, next, &wmi_block_list, list) {
list_del(&wblock->list); if (wblock->acpi_device == device) {
if (wblock->dev.class) list_del(&wblock->list);
device_unregister(&wblock->dev); device_unregister(&wblock->dev.dev);
else }
kfree(wblock);
} }
} }
static bool guid_already_parsed(const char *guid_string) static bool guid_already_parsed(struct acpi_device *device,
const u8 *guid)
{ {
struct wmi_block *wblock; struct wmi_block *wblock;
list_for_each_entry(wblock, &wmi_block_list, list) list_for_each_entry(wblock, &wmi_block_list, list) {
if (memcmp(wblock->gblock.guid, guid_string, 16) == 0) if (memcmp(wblock->gblock.guid, guid, 16) == 0) {
/*
* Because we historically didn't track the relationship
* between GUIDs and ACPI nodes, we don't know whether
* we need to suppress GUIDs that are unique on a
* given node but duplicated across nodes.
*/
dev_warn(&device->dev, "duplicate WMI GUID %pUL (first instance was on %s)\n",
guid, dev_name(&wblock->acpi_device->dev));
return true; return true;
}
}
return false; return false;
} }
...@@ -645,17 +936,17 @@ static bool guid_already_parsed(const char *guid_string) ...@@ -645,17 +936,17 @@ static bool guid_already_parsed(const char *guid_string)
/* /*
* Parse the _WDG method for the GUID data blocks * Parse the _WDG method for the GUID data blocks
*/ */
static int parse_wdg(acpi_handle handle) static int parse_wdg(struct device *wmi_bus_dev, struct acpi_device *device)
{ {
struct acpi_buffer out = {ACPI_ALLOCATE_BUFFER, NULL}; struct acpi_buffer out = {ACPI_ALLOCATE_BUFFER, NULL};
union acpi_object *obj;
const struct guid_block *gblock; const struct guid_block *gblock;
struct wmi_block *wblock; struct wmi_block *wblock, *next;
union acpi_object *obj;
acpi_status status; acpi_status status;
int retval; int retval = 0;
u32 i, total; u32 i, total;
status = acpi_evaluate_object(handle, "_WDG", NULL, &out); status = acpi_evaluate_object(device->handle, "_WDG", NULL, &out);
if (ACPI_FAILURE(status)) if (ACPI_FAILURE(status))
return -ENXIO; return -ENXIO;
...@@ -675,25 +966,28 @@ static int parse_wdg(acpi_handle handle) ...@@ -675,25 +966,28 @@ static int parse_wdg(acpi_handle handle)
if (debug_dump_wdg) if (debug_dump_wdg)
wmi_dump_wdg(&gblock[i]); wmi_dump_wdg(&gblock[i]);
/*
* Some WMI devices, like those for nVidia hooks, have a
* duplicate GUID. It's not clear what we should do in this
* case yet, so for now, we'll just ignore the duplicate
* for device creation.
*/
if (guid_already_parsed(device, gblock[i].guid))
continue;
wblock = kzalloc(sizeof(struct wmi_block), GFP_KERNEL); wblock = kzalloc(sizeof(struct wmi_block), GFP_KERNEL);
if (!wblock) if (!wblock) {
return -ENOMEM; retval = -ENOMEM;
break;
}
wblock->handle = handle; wblock->acpi_device = device;
wblock->gblock = gblock[i]; wblock->gblock = gblock[i];
/* retval = wmi_create_device(wmi_bus_dev, &gblock[i], wblock, device);
Some WMI devices, like those for nVidia hooks, have a if (retval) {
duplicate GUID. It's not clear what we should do in this kfree(wblock);
case yet, so for now, we'll just ignore the duplicate continue;
for device creation.
*/
if (!guid_already_parsed(gblock[i].guid)) {
retval = wmi_create_device(&gblock[i], wblock, handle);
if (retval) {
wmi_free_devices();
goto out_free_pointer;
}
} }
list_add_tail(&wblock->list, &wmi_block_list); list_add_tail(&wblock->list, &wmi_block_list);
...@@ -704,11 +998,27 @@ static int parse_wdg(acpi_handle handle) ...@@ -704,11 +998,27 @@ static int parse_wdg(acpi_handle handle)
} }
} }
retval = 0; /*
* Now that all of the devices are created, add them to the
* device tree and probe subdrivers.
*/
list_for_each_entry_safe(wblock, next, &wmi_block_list, list) {
if (wblock->acpi_device != device)
continue;
retval = device_add(&wblock->dev.dev);
if (retval) {
dev_err(wmi_bus_dev, "failed to register %pULL\n",
wblock->gblock.guid);
if (debug_event)
wmi_method_enable(wblock, 0);
list_del(&wblock->list);
put_device(&wblock->dev.dev);
}
}
out_free_pointer: out_free_pointer:
kfree(out.pointer); kfree(out.pointer);
return retval; return retval;
} }
...@@ -756,67 +1066,168 @@ acpi_wmi_ec_space_handler(u32 function, acpi_physical_address address, ...@@ -756,67 +1066,168 @@ acpi_wmi_ec_space_handler(u32 function, acpi_physical_address address,
} }
} }
static void acpi_wmi_notify(struct acpi_device *device, u32 event) static void acpi_wmi_notify_handler(acpi_handle handle, u32 event,
void *context)
{ {
struct guid_block *block; struct guid_block *block;
struct wmi_block *wblock; struct wmi_block *wblock;
struct list_head *p; struct list_head *p;
bool found_it = false;
list_for_each(p, &wmi_block_list) { list_for_each(p, &wmi_block_list) {
wblock = list_entry(p, struct wmi_block, list); wblock = list_entry(p, struct wmi_block, list);
block = &wblock->gblock; block = &wblock->gblock;
if ((block->flags & ACPI_WMI_EVENT) && if (wblock->acpi_device->handle == handle &&
(block->notify_id == event)) { (block->flags & ACPI_WMI_EVENT) &&
if (wblock->handler) (block->notify_id == event))
wblock->handler(event, wblock->handler_data); {
if (debug_event) { found_it = true;
pr_info("DEBUG Event GUID: %pUL\n",
wblock->gblock.guid);
}
acpi_bus_generate_netlink_event(
device->pnp.device_class, dev_name(&device->dev),
event, 0);
break; break;
} }
} }
if (!found_it)
return;
/* If a driver is bound, then notify the driver. */
if (wblock->dev.dev.driver) {
struct wmi_driver *driver;
struct acpi_object_list input;
union acpi_object params[1];
struct acpi_buffer evdata = { ACPI_ALLOCATE_BUFFER, NULL };
acpi_status status;
driver = container_of(wblock->dev.dev.driver,
struct wmi_driver, driver);
input.count = 1;
input.pointer = params;
params[0].type = ACPI_TYPE_INTEGER;
params[0].integer.value = event;
status = acpi_evaluate_object(wblock->acpi_device->handle,
"_WED", &input, &evdata);
if (ACPI_FAILURE(status)) {
dev_warn(&wblock->dev.dev,
"failed to get event data\n");
return;
}
if (driver->notify)
driver->notify(&wblock->dev,
(union acpi_object *)evdata.pointer);
kfree(evdata.pointer);
} else if (wblock->handler) {
/* Legacy handler */
wblock->handler(event, wblock->handler_data);
}
if (debug_event) {
pr_info("DEBUG Event GUID: %pUL\n",
wblock->gblock.guid);
}
acpi_bus_generate_netlink_event(
wblock->acpi_device->pnp.device_class,
dev_name(&wblock->dev.dev),
event, 0);
} }
static int acpi_wmi_remove(struct acpi_device *device) static int acpi_wmi_remove(struct platform_device *device)
{ {
acpi_remove_address_space_handler(device->handle, struct acpi_device *acpi_device = ACPI_COMPANION(&device->dev);
acpi_remove_notify_handler(acpi_device->handle, ACPI_DEVICE_NOTIFY,
acpi_wmi_notify_handler);
acpi_remove_address_space_handler(acpi_device->handle,
ACPI_ADR_SPACE_EC, &acpi_wmi_ec_space_handler); ACPI_ADR_SPACE_EC, &acpi_wmi_ec_space_handler);
wmi_free_devices(); wmi_free_devices(acpi_device);
device_unregister((struct device *)dev_get_drvdata(&device->dev));
return 0; return 0;
} }
static int acpi_wmi_add(struct acpi_device *device) static int acpi_wmi_probe(struct platform_device *device)
{ {
struct acpi_device *acpi_device;
struct device *wmi_bus_dev;
acpi_status status; acpi_status status;
int error; int error;
status = acpi_install_address_space_handler(device->handle, acpi_device = ACPI_COMPANION(&device->dev);
if (!acpi_device) {
dev_err(&device->dev, "ACPI companion is missing\n");
return -ENODEV;
}
status = acpi_install_address_space_handler(acpi_device->handle,
ACPI_ADR_SPACE_EC, ACPI_ADR_SPACE_EC,
&acpi_wmi_ec_space_handler, &acpi_wmi_ec_space_handler,
NULL, NULL); NULL, NULL);
if (ACPI_FAILURE(status)) { if (ACPI_FAILURE(status)) {
pr_err("Error installing EC region handler\n"); dev_err(&device->dev, "Error installing EC region handler\n");
return -ENODEV; return -ENODEV;
} }
error = parse_wdg(device->handle); status = acpi_install_notify_handler(acpi_device->handle,
ACPI_DEVICE_NOTIFY,
acpi_wmi_notify_handler,
NULL);
if (ACPI_FAILURE(status)) {
dev_err(&device->dev, "Error installing notify handler\n");
error = -ENODEV;
goto err_remove_ec_handler;
}
wmi_bus_dev = device_create(&wmi_bus_class, &device->dev, MKDEV(0, 0),
NULL, "wmi_bus-%s", dev_name(&device->dev));
if (IS_ERR(wmi_bus_dev)) {
error = PTR_ERR(wmi_bus_dev);
goto err_remove_notify_handler;
}
dev_set_drvdata(&device->dev, wmi_bus_dev);
error = parse_wdg(wmi_bus_dev, acpi_device);
if (error) { if (error) {
acpi_remove_address_space_handler(device->handle,
ACPI_ADR_SPACE_EC,
&acpi_wmi_ec_space_handler);
pr_err("Failed to parse WDG method\n"); pr_err("Failed to parse WDG method\n");
return error; goto err_remove_busdev;
} }
return 0; return 0;
err_remove_busdev:
device_unregister(wmi_bus_dev);
err_remove_notify_handler:
acpi_remove_notify_handler(acpi_device->handle, ACPI_DEVICE_NOTIFY,
acpi_wmi_notify_handler);
err_remove_ec_handler:
acpi_remove_address_space_handler(acpi_device->handle,
ACPI_ADR_SPACE_EC,
&acpi_wmi_ec_space_handler);
return error;
}
int __must_check __wmi_driver_register(struct wmi_driver *driver,
struct module *owner)
{
driver->driver.owner = owner;
driver->driver.bus = &wmi_bus_type;
return driver_register(&driver->driver);
} }
EXPORT_SYMBOL(__wmi_driver_register);
void wmi_driver_unregister(struct wmi_driver *driver)
{
driver_unregister(&driver->driver);
}
EXPORT_SYMBOL(wmi_driver_unregister);
static int __init acpi_wmi_init(void) static int __init acpi_wmi_init(void)
{ {
...@@ -825,27 +1236,36 @@ static int __init acpi_wmi_init(void) ...@@ -825,27 +1236,36 @@ static int __init acpi_wmi_init(void)
if (acpi_disabled) if (acpi_disabled)
return -ENODEV; return -ENODEV;
error = class_register(&wmi_class); error = class_register(&wmi_bus_class);
if (error) if (error)
return error; return error;
error = acpi_bus_register_driver(&acpi_wmi_driver); error = bus_register(&wmi_bus_type);
if (error)
goto err_unreg_class;
error = platform_driver_register(&acpi_wmi_driver);
if (error) { if (error) {
pr_err("Error loading mapper\n"); pr_err("Error loading mapper\n");
class_unregister(&wmi_class); goto err_unreg_bus;
return error;
} }
pr_info("Mapper loaded\n");
return 0; return 0;
err_unreg_class:
class_unregister(&wmi_bus_class);
err_unreg_bus:
bus_unregister(&wmi_bus_type);
return error;
} }
static void __exit acpi_wmi_exit(void) static void __exit acpi_wmi_exit(void)
{ {
acpi_bus_unregister_driver(&acpi_wmi_driver); platform_driver_unregister(&acpi_wmi_driver);
class_unregister(&wmi_class); class_unregister(&wmi_bus_class);
bus_unregister(&wmi_bus_type);
pr_info("Mapper unloaded\n");
} }
subsys_initcall(acpi_wmi_init); subsys_initcall(acpi_wmi_init);
......
/*
* wmi.h - ACPI WMI interface
*
* Copyright (c) 2015 Andrew Lutomirski
*
* 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.
*/
#ifndef _LINUX_WMI_H
#define _LINUX_WMI_H
#include <linux/device.h>
#include <linux/acpi.h>
struct wmi_device {
struct device dev;
/* True for data blocks implementing the Set Control Method */
bool setable;
};
/* 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);
struct wmi_device_id {
const char *guid_string;
};
struct wmi_driver {
struct device_driver driver;
const struct wmi_device_id *id_table;
int (*probe)(struct wmi_device *wdev);
int (*remove)(struct wmi_device *wdev);
void (*notify)(struct wmi_device *device, union acpi_object *data);
};
extern int __must_check __wmi_driver_register(struct wmi_driver *driver,
struct module *owner);
extern void wmi_driver_unregister(struct wmi_driver *driver);
#define wmi_driver_register(driver) __wmi_driver_register((driver), THIS_MODULE)
#define module_wmi_driver(__wmi_driver) \
module_driver(__wmi_driver, wmi_driver_register, \
wmi_driver_unregister)
#endif
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