diff options
Diffstat (limited to 'drivers/platform')
27 files changed, 1376 insertions, 671 deletions
diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig index a3ccc3c795a5..b04860703740 100644 --- a/drivers/platform/x86/Kconfig +++ b/drivers/platform/x86/Kconfig @@ -195,16 +195,6 @@ config FUJITSU_LAPTOP 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 tristate "Fujitsu Tablet Extras" depends on ACPI @@ -656,6 +646,18 @@ config ACPI_WMI It is safe to enable this driver even if your DSDT doesn't define 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 tristate "MSI WMI extras" depends on ACPI_WMI @@ -669,6 +671,13 @@ config MSI_WMI To compile this driver as a module, choose M here: the module will 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 tristate "Topstar Laptop Extras" depends on ACPI diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile index ab22ce77fb66..91cec1751461 100644 --- a/drivers/platform/x86/Makefile +++ b/drivers/platform/x86/Makefile @@ -35,8 +35,10 @@ obj-$(CONFIG_PANASONIC_LAPTOP) += panasonic-laptop.o obj-$(CONFIG_INTEL_MENLOW) += intel_menlow.o obj-$(CONFIG_ACPI_WMI) += 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_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 # before toshiba_acpi initializes diff --git a/drivers/platform/x86/acer-wmi.c b/drivers/platform/x86/acer-wmi.c index 79fa5ab3fd00..1be71f956d5c 100644 --- a/drivers/platform/x86/acer-wmi.c +++ b/drivers/platform/x86/acer-wmi.c @@ -149,6 +149,8 @@ struct event_return_value { #define ACER_WMID3_GDS_THREEG (1<<6) /* 3G */ #define ACER_WMID3_GDS_WIMAX (1<<7) /* WiMAX */ #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 */ /* Hotkey Customized Setting and Acer Application Status. @@ -221,6 +223,7 @@ struct hotkey_function_type_aa { #define ACER_CAP_BRIGHTNESS (1<<3) #define ACER_CAP_THREEG (1<<4) #define ACER_CAP_ACCEL (1<<5) +#define ACER_CAP_RFBTN (1<<6) #define ACER_CAP_ANY (0xFFFFFFFF) /* @@ -700,7 +703,7 @@ struct acpi_buffer *result) input.length = sizeof(struct wmab_args); 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; } @@ -965,7 +968,7 @@ WMI_execute_u32(u32 method_id, u32 in, u32 *out) u32 tmp = 0; 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)) return status; @@ -1264,6 +1267,10 @@ static void __init type_aa_dmi_decode(const struct dmi_header *header, void *d) interface->capability |= ACER_CAP_THREEG; if (type_aa->commun_func_bitmap & ACER_WMID3_GDS_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; } @@ -1275,7 +1282,7 @@ static acpi_status __init WMID_set_capabilities(void) acpi_status status; u32 devices; - status = wmi_query_block(WMID_GUID2, 1, &out); + status = wmi_query_block(WMID_GUID2, 0, &out); if (ACPI_FAILURE(status)) return status; @@ -2018,7 +2025,7 @@ static u32 get_wmid_devices(void) acpi_status status; u32 devices = 0; - status = wmi_query_block(WMID_GUID2, 1, &out); + status = wmi_query_block(WMID_GUID2, 0, &out); if (ACPI_FAILURE(status)) return 0; diff --git a/drivers/platform/x86/acerhdf.c b/drivers/platform/x86/acerhdf.c index 2acdb0d6ea89..ea22591ee66f 100644 --- a/drivers/platform/x86/acerhdf.c +++ b/drivers/platform/x86/acerhdf.c @@ -557,7 +557,7 @@ err_out: } /* 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_cur_state = acerhdf_get_cur_state, .set_cur_state = acerhdf_set_cur_state, diff --git a/drivers/platform/x86/alienware-wmi.c b/drivers/platform/x86/alienware-wmi.c index d6b34923fb4e..9866fec78c1c 100644 --- a/drivers/platform/x86/alienware-wmi.c +++ b/drivers/platform/x86/alienware-wmi.c @@ -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); - status = wmi_evaluate_method(guid, 1, method_id, &input, NULL); + status = wmi_evaluate_method(guid, 0, method_id, &input, NULL); if (ACPI_FAILURE(status)) pr_err("alienware-wmi: zone set failure: %u\n", status); return ACPI_FAILURE(status); @@ -352,7 +352,7 @@ static int wmax_brightness(int brightness) }; input.length = (acpi_size) sizeof(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); if (ACPI_FAILURE(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, if (out_data != NULL) { output.length = ACPI_ALLOCATE_BUFFER; output.pointer = NULL; - status = wmi_evaluate_method(WMAX_CONTROL_GUID, 1, + status = wmi_evaluate_method(WMAX_CONTROL_GUID, 0, command, &input, &output); } else - status = wmi_evaluate_method(WMAX_CONTROL_GUID, 1, + status = wmi_evaluate_method(WMAX_CONTROL_GUID, 0, command, &input, NULL); if (ACPI_SUCCESS(status) && out_data != NULL) { diff --git a/drivers/platform/x86/dell-laptop.c b/drivers/platform/x86/dell-laptop.c index ec202094bd50..f42159fd2031 100644 --- a/drivers/platform/x86/dell-laptop.c +++ b/drivers/platform/x86/dell-laptop.c @@ -1510,7 +1510,11 @@ static void kbd_init(void) ret = kbd_init_info(); 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; } diff --git a/drivers/platform/x86/dell-rbtn.c b/drivers/platform/x86/dell-rbtn.c index dcd9f40a4b18..f3afe778001e 100644 --- a/drivers/platform/x86/dell-rbtn.c +++ b/drivers/platform/x86/dell-rbtn.c @@ -110,7 +110,7 @@ static int rbtn_rfkill_set_block(void *data, bool blocked) return -EINVAL; } -static struct rfkill_ops rbtn_ops = { +static const struct rfkill_ops rbtn_ops = { .query = rbtn_rfkill_query, .set_block = rbtn_rfkill_set_block, }; @@ -221,16 +221,27 @@ static const struct acpi_device_id rbtn_ids[] = { /* * This driver can also handle the "DELLABC6" device that - * appears on the XPS 13 9350, but that device is disabled - * by the DSDT unless booted with acpi_osi="!Windows 2012" - * acpi_osi="!Windows 2013". Even if we boot that and bind - * the driver, we seem to have inconsistent behavior in - * which NetworkManager can get out of sync with the rfkill - * state. + * appears on the XPS 13 9350, but that device is disabled by + * the DSDT unless booted with acpi_osi="!Windows 2012" + * acpi_osi="!Windows 2013". * - * On the XPS 13 9350 and similar laptops, we're not supposed to - * use DELLABC6 at all. Instead, we handle the rfkill button - * via the intel-hid driver. + * According to Mario at Dell: + * + * 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 }, diff --git a/drivers/platform/x86/dell-wmi-led.c b/drivers/platform/x86/dell-wmi-led.c index a0c7e99530ef..5bedaf7f0633 100644 --- a/drivers/platform/x86/dell-wmi-led.c +++ b/drivers/platform/x86/dell-wmi-led.c @@ -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.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)) return status; diff --git a/drivers/platform/x86/dell-wmi.c b/drivers/platform/x86/dell-wmi.c index 8a64c7967753..f8978464df31 100644 --- a/drivers/platform/x86/dell-wmi.c +++ b/drivers/platform/x86/dell-wmi.c @@ -36,6 +36,7 @@ #include <linux/acpi.h> #include <linux/string.h> #include <linux/dmi.h> +#include <linux/wmi.h> #include <acpi/video.h> #include "dell-smbios.h" @@ -53,6 +54,10 @@ static bool wmi_requires_smbios_request; MODULE_ALIAS("wmi:"DELL_EVENT_GUID); MODULE_ALIAS("wmi:"DELL_DESCRIPTOR_GUID); +struct dell_wmi_priv { + struct input_dev *input_dev; +}; + static int __init dmi_matched(const struct dmi_system_id *dmi) { wmi_requires_smbios_request = 1; @@ -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 * 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 } }, /* Key code is followed by brightness level */ @@ -207,7 +212,7 @@ struct dell_dmi_results { }; /* 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, [1] = KEY_NEXTSONG, [2] = KEY_PLAYPAUSE, @@ -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 * 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 */ { KE_IGNORE, 0x151, { KEY_RESERVED } }, @@ -272,7 +277,12 @@ static const struct key_entry dell_wmi_keymap_type_0010[] __initconst = { /* RGB keyboard backlight control */ { 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 } }, /* Rugged magnetic dock attach/detach events */ @@ -289,7 +299,7 @@ static const struct key_entry dell_wmi_keymap_type_0010[] __initconst = { /* * 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 */ { KE_IGNORE, 0xfff0, { KEY_RESERVED } }, @@ -304,13 +314,12 @@ static const struct key_entry dell_wmi_keymap_type_0011[] __initconst = { { KE_IGNORE, 0x02f6, { KEY_RESERVED } }, }; -static struct input_dev *dell_wmi_input_dev; - -static void dell_wmi_process_key(int type, int code) +static void dell_wmi_process_key(struct wmi_device *wdev, int type, int code) { + struct dell_wmi_priv *priv = dev_get_drvdata(&wdev->dev); 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); if (!key) { 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) dell_laptop_call_notifier( 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; + acpi_size buffer_size; 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) { pr_warn("bad response type %x\n", obj->type); - kfree(obj); return; } @@ -404,13 +398,14 @@ static void dell_wmi_notify(u32 value, void *context) switch (buffer_entry[1]) { case 0x0000: /* One key pressed or event occurred */ 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 */ break; case 0x0010: /* Sequence of keys pressed */ case 0x0011: /* Sequence of events occurred */ for (i = 2; i < len; ++i) - dell_wmi_process_key(buffer_entry[1], + dell_wmi_process_key(wdev, buffer_entry[1], buffer_entry[i]); break; default: /* Unknown event */ @@ -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) @@ -437,9 +431,7 @@ static bool have_scancode(u32 scancode, const struct key_entry *keymap, int len) return false; } -static void __init handle_dmi_entry(const struct dmi_header *dm, - void *opaque) - +static void handle_dmi_entry(const struct dmi_header *dm, void *opaque) { struct dell_dmi_results *results = opaque; struct dell_bios_hotkey_table *table; @@ -449,6 +441,7 @@ static void __init handle_dmi_entry(const struct dmi_header *dm, if (results->err || results->keymap) return; /* We already found the hotkey table. */ + /* The Dell hotkey table is type 0xB2. Scan until we find it. */ if (dm->type != 0xb2) return; @@ -509,19 +502,20 @@ static void __init handle_dmi_entry(const struct dmi_header *dm, 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 key_entry *keymap; int err, i, pos = 0; - dell_wmi_input_dev = input_allocate_device(); - if (!dell_wmi_input_dev) + priv->input_dev = input_allocate_device(); + if (!priv->input_dev) return -ENOMEM; - dell_wmi_input_dev->name = "Dell WMI hotkeys"; - dell_wmi_input_dev->phys = "wmi/input0"; - dell_wmi_input_dev->id.bustype = BUS_HOST; + priv->input_dev->name = "Dell WMI hotkeys"; + priv->input_dev->id.bustype = BUS_HOST; + priv->input_dev->dev.parent = &wdev->dev; if (dmi_walk(handle_dmi_entry, &dmi_results)) { /* @@ -596,7 +590,7 @@ static int __init dell_wmi_input_setup(void) 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 * original one that was allocated. @@ -605,17 +599,24 @@ static int __init dell_wmi_input_setup(void) if (err) goto err_free_dev; - err = input_register_device(dell_wmi_input_dev); + err = input_register_device(priv->input_dev); if (err) goto err_free_dev; return 0; err_free_dev: - input_free_device(dell_wmi_input_dev); + input_free_device(priv->input_dev); 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: * @@ -714,46 +715,55 @@ static int dell_wmi_events_set_enabled(bool enable) 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) { 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(); if (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); if (wmi_requires_smbios_request) { err = dell_wmi_events_set_enabled(true); if (err) { 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 0; + return wmi_driver_register(&dell_wmi_driver); } module_init(dell_wmi_init); @@ -761,7 +771,7 @@ static void __exit dell_wmi_exit(void) { if (wmi_requires_smbios_request) 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); diff --git a/drivers/platform/x86/eeepc-laptop.c b/drivers/platform/x86/eeepc-laptop.c index 2426399e1e04..5a681962899c 100644 --- a/drivers/platform/x86/eeepc-laptop.c +++ b/drivers/platform/x86/eeepc-laptop.c @@ -445,7 +445,7 @@ static struct attribute *platform_attributes[] = { NULL }; -static struct attribute_group platform_attribute_group = { +static const struct attribute_group platform_attribute_group = { .attrs = platform_attributes }; diff --git a/drivers/platform/x86/fujitsu-laptop.c b/drivers/platform/x86/fujitsu-laptop.c index 7f49d92914c9..c1a852847d02 100644 --- a/drivers/platform/x86/fujitsu-laptop.c +++ b/drivers/platform/x86/fujitsu-laptop.c @@ -112,25 +112,8 @@ #define MAX_HOTKEY_RINGBUFFER_SIZE 100 #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 */ struct fujitsu_bl { - acpi_handle acpi_handle; struct input_dev *input; char phys[32]; struct backlight_device *bl_device; @@ -144,8 +127,6 @@ static bool disable_brightness_adjust; /* Device used to access hotkeys and other features on the laptop */ struct fujitsu_laptop { - acpi_handle acpi_handle; - struct acpi_device *dev; struct input_dev *input; char phys[32]; struct platform_device *pf_device; @@ -155,15 +136,12 @@ struct fujitsu_laptop { int flags_state; }; -static struct fujitsu_laptop *fujitsu_laptop; - -#ifdef CONFIG_FUJITSU_LAPTOP_DEBUG -static u32 dbg_level = 0x03; -#endif +static struct acpi_device *fext; /* 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] = { { .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) unsigned long long value; acpi_status status; - status = acpi_evaluate_integer(fujitsu_laptop->acpi_handle, "FUNC", - &arg_list, &value); + status = acpi_evaluate_integer(device->handle, "FUNC", &arg_list, + &value); 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; } - vdbg_printk(FUJLAPTOP_DBG_TRACE, "FUNC 0x%x (args 0x%x, 0x%x, 0x%x) returned 0x%x\n", - func, op, feature, state, (int)value); + acpi_handle_debug(device->handle, + "FUNC 0x%x (args 0x%x, 0x%x, 0x%x) returned 0x%x\n", + func, op, feature, state, (int)value); return value; } /* 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; char *method; switch (use_alt_lcd_levels) { case -1: - if (acpi_has_method(fujitsu_bl->acpi_handle, "SBL2")) + if (acpi_has_method(device->handle, "SBL2")) method = "SBL2"; else method = "SBLL"; @@ -209,74 +189,77 @@ static int set_lcd_level(int level) break; } - vdbg_printk(FUJLAPTOP_DBG_TRACE, "set lcd level via %s [%d]\n", - method, level); + acpi_handle_debug(device->handle, "set lcd level via %s [%d]\n", method, + level); - if (level < 0 || level >= fujitsu_bl->max_brightness) + if (level < 0 || level >= priv->max_brightness) return -EINVAL; - status = acpi_execute_simple_method(fujitsu_bl->acpi_handle, method, - level); + status = acpi_execute_simple_method(device->handle, method, level); if (ACPI_FAILURE(status)) { - vdbg_printk(FUJLAPTOP_DBG_ERROR, "Failed to evaluate %s\n", - method); + acpi_handle_err(device->handle, "Failed to evaluate %s\n", + method); return -ENODEV; } - fujitsu_bl->brightness_level = level; + priv->brightness_level = level; 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; 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, - &state); + status = acpi_evaluate_integer(device->handle, "GBLL", NULL, &state); if (ACPI_FAILURE(status)) 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; 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, - &state); + status = acpi_evaluate_integer(device->handle, "RBLL", NULL, &state); if (ACPI_FAILURE(status)) return -1; - fujitsu_bl->max_brightness = state; + priv->max_brightness = state; - return fujitsu_bl->max_brightness; + return priv->max_brightness; } /* Backlight device stuff */ 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) { + struct acpi_device *device = bl_get_data(b); + 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 - 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 = { @@ -287,9 +270,11 @@ static const struct backlight_ops fujitsu_bl_ops = { static ssize_t lid_show(struct device *dev, struct device_attribute *attr, 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"); - if (fujitsu_laptop->flags_state & FLAG_LID) + if (priv->flags_state & FLAG_LID) return sprintf(buf, "open\n"); else return sprintf(buf, "closed\n"); @@ -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, 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"); - if (fujitsu_laptop->flags_state & FLAG_DOCK) + if (priv->flags_state & FLAG_DOCK) return sprintf(buf, "docked\n"); else return sprintf(buf, "undocked\n"); @@ -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, 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"); - if (fujitsu_laptop->flags_state & FLAG_RFKILL) + if (priv->flags_state & FLAG_RFKILL) return sprintf(buf, "on\n"); else return sprintf(buf, "killed\n"); @@ -348,89 +337,76 @@ static const struct key_entry keymap_backlight[] = { 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; - fujitsu_bl->input = devm_input_allocate_device(&device->dev); - if (!fujitsu_bl->input) + priv->input = devm_input_allocate_device(&device->dev); + if (!priv->input) return -ENOMEM; - snprintf(fujitsu_bl->phys, sizeof(fujitsu_bl->phys), - "%s/video/input0", acpi_device_hid(device)); + snprintf(priv->phys, sizeof(priv->phys), "%s/video/input0", + acpi_device_hid(device)); - fujitsu_bl->input->name = acpi_device_name(device); - fujitsu_bl->input->phys = fujitsu_bl->phys; - fujitsu_bl->input->id.bustype = BUS_HOST; - fujitsu_bl->input->id.product = 0x06; + priv->input->name = acpi_device_name(device); + priv->input->phys = priv->phys; + priv->input->id.bustype = BUS_HOST; + 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) return ret; - return input_register_device(fujitsu_bl->input); + return input_register_device(priv->input); } static int fujitsu_backlight_register(struct acpi_device *device) { + struct fujitsu_bl *priv = acpi_driver_data(device); const struct backlight_properties props = { - .brightness = fujitsu_bl->brightness_level, - .max_brightness = fujitsu_bl->max_brightness - 1, + .brightness = priv->brightness_level, + .max_brightness = priv->max_brightness - 1, .type = BACKLIGHT_PLATFORM }; struct backlight_device *bd; bd = devm_backlight_device_register(&device->dev, "fujitsu-laptop", - &device->dev, NULL, + &device->dev, device, &fujitsu_bl_ops, &props); if (IS_ERR(bd)) return PTR_ERR(bd); - fujitsu_bl->bl_device = bd; + priv->bl_device = bd; return 0; } static int acpi_fujitsu_bl_add(struct acpi_device *device) { - int state = 0; + struct fujitsu_bl *priv; int error; if (acpi_video_get_backlight_type() != acpi_backlight_vendor) return -ENODEV; - if (!device) - return -EINVAL; + priv = devm_kzalloc(&device->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; - fujitsu_bl->acpi_handle = device->handle; - sprintf(acpi_device_name(device), "%s", ACPI_FUJITSU_BL_DEVICE_NAME); - sprintf(acpi_device_class(device), "%s", ACPI_FUJITSU_CLASS); - device->driver_data = fujitsu_bl; + fujitsu_bl = priv; + strcpy(acpi_device_name(device), ACPI_FUJITSU_BL_DEVICE_NAME); + strcpy(acpi_device_class(device), ACPI_FUJITSU_CLASS); + device->driver_data = priv; error = acpi_fujitsu_bl_input_setup(device); if (error) return error; - error = acpi_bus_update_power(fujitsu_bl->acpi_handle, &state); - if (error) { - 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"); + pr_info("ACPI: %s [%s]\n", + acpi_device_name(device), acpi_device_bid(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"); - } - - if (get_max_brightness() <= 0) - fujitsu_bl->max_brightness = FUJITSU_LCD_N_LEVELS; - get_lcd_level(); + if (get_max_brightness(device) <= 0) + priv->max_brightness = FUJITSU_LCD_N_LEVELS; + get_lcd_level(device); error = fujitsu_backlight_register(device); if (error) @@ -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) { - struct input_dev *input; + struct fujitsu_bl *priv = acpi_driver_data(device); int oldb, newb; - input = fujitsu_bl->input; - if (event != ACPI_FUJITSU_NOTIFY_CODE1) { - vdbg_printk(FUJLAPTOP_DBG_WARN, - "unsupported event [0x%x]\n", event); - sparse_keymap_report_event(input, -1, 1, true); + acpi_handle_info(device->handle, "unsupported event [0x%x]\n", + event); + sparse_keymap_report_event(priv->input, -1, 1, true); return; } - oldb = fujitsu_bl->brightness_level; - get_lcd_level(); - newb = fujitsu_bl->brightness_level; + oldb = priv->brightness_level; + get_lcd_level(device); + newb = priv->brightness_level; - vdbg_printk(FUJLAPTOP_DBG_TRACE, "brightness button event [%i -> %i]\n", - oldb, newb); + acpi_handle_debug(device->handle, + "brightness button event [%i -> %i]\n", oldb, newb); if (oldb == newb) return; 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 */ @@ -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) { - struct fujitsu_laptop *fujitsu_laptop = acpi_driver_data(device); + struct fujitsu_laptop *priv = acpi_driver_data(device); int ret; - fujitsu_laptop->input = devm_input_allocate_device(&device->dev); - if (!fujitsu_laptop->input) + priv->input = devm_input_allocate_device(&device->dev); + if (!priv->input) return -ENOMEM; - snprintf(fujitsu_laptop->phys, sizeof(fujitsu_laptop->phys), - "%s/video/input0", acpi_device_hid(device)); + snprintf(priv->phys, sizeof(priv->phys), "%s/input0", + acpi_device_hid(device)); - fujitsu_laptop->input->name = acpi_device_name(device); - fujitsu_laptop->input->phys = fujitsu_laptop->phys; - fujitsu_laptop->input->id.bustype = BUS_HOST; - fujitsu_laptop->input->id.product = 0x06; + priv->input->name = acpi_device_name(device); + priv->input->phys = priv->phys; + priv->input->id.bustype = BUS_HOST; 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) 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; - fujitsu_laptop->pf_device = platform_device_alloc("fujitsu-laptop", -1); - if (!fujitsu_laptop->pf_device) + priv->pf_device = platform_device_alloc("fujitsu-laptop", -1); + if (!priv->pf_device) 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) 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); if (ret) goto err_del_platform_device; @@ -584,23 +560,26 @@ static int fujitsu_laptop_platform_add(void) return 0; err_del_platform_device: - platform_device_del(fujitsu_laptop->pf_device); + platform_device_del(priv->pf_device); err_put_platform_device: - platform_device_put(fujitsu_laptop->pf_device); + platform_device_put(priv->pf_device); 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); - platform_device_unregister(fujitsu_laptop->pf_device); + platform_device_unregister(priv->pf_device); } static int logolamp_set(struct led_classdev *cdev, enum led_brightness brightness) { + struct acpi_device *device = to_acpi_device(cdev->dev->parent); int poweron = FUNC_LED_ON, always = FUNC_LED_ON; int ret; @@ -610,132 +589,128 @@ static int logolamp_set(struct led_classdev *cdev, if (brightness < LED_FULL) 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) 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) { + struct acpi_device *device = to_acpi_device(cdev->dev->parent); 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) 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) return LED_HALF; 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, enum led_brightness brightness) { + struct acpi_device *device = to_acpi_device(cdev->dev->parent); + 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); else - return call_fext_func(FUNC_LEDS, 0x1, KEYBOARD_LAMPS, + return call_fext_func(device, FUNC_LEDS, 0x1, KEYBOARD_LAMPS, FUNC_LED_OFF); } 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; - 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; 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, enum led_brightness brightness) { + struct acpi_device *device = to_acpi_device(cdev->dev->parent); + 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); 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) { + struct acpi_device *device = to_acpi_device(cdev->dev->parent); 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; 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, enum led_brightness brightness) { + struct acpi_device *device = to_acpi_device(cdev->dev->parent); 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) - return call_fext_func(FUNC_LEDS, 0x1, ECO_LED, + return call_fext_func(device, FUNC_LEDS, 0x1, ECO_LED, curr | ECO_LED_ON); else - return call_fext_func(FUNC_LEDS, 0x1, ECO_LED, + return call_fext_func(device, FUNC_LEDS, 0x1, ECO_LED, curr & ~ECO_LED_ON); } 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; - 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; 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) { + struct led_classdev *led; int result; - if (call_fext_func(FUNC_LEDS, 0x0, 0x0, 0x0) & LOGOLAMP_POWERON) { - result = devm_led_classdev_register(&device->dev, - &logolamp_led); + if (call_fext_func(device, + FUNC_LEDS, 0x0, 0x0, 0x0) & LOGOLAMP_POWERON) { + 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) return result; } - if ((call_fext_func(FUNC_LEDS, 0x0, 0x0, 0x0) & KEYBOARD_LAMPS) && - (call_fext_func(FUNC_BUTTONS, 0x0, 0x0, 0x0) == 0x0)) { - result = devm_led_classdev_register(&device->dev, &kblamps_led); + if ((call_fext_func(device, + FUNC_LEDS, 0x0, 0x0, 0x0) & KEYBOARD_LAMPS) && + (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) return result; } @@ -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 * that an RF LED is present. */ - if (call_fext_func(FUNC_BUTTONS, 0x0, 0x0, 0x0) & BIT(24)) { - result = devm_led_classdev_register(&device->dev, &radio_led); + if (call_fext_func(device, FUNC_BUTTONS, 0x0, 0x0, 0x0) & BIT(24)) { + 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) return result; } @@ -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. * Confirm by testing the status. */ - if ((call_fext_func(FUNC_LEDS, 0x0, 0x0, 0x0) & BIT(14)) && - (call_fext_func(FUNC_LEDS, 0x2, ECO_LED, 0x0) != UNSUPPORTED_CMD)) { - result = devm_led_classdev_register(&device->dev, &eco_led); + if ((call_fext_func(device, FUNC_LEDS, 0x0, 0x0, 0x0) & BIT(14)) && + (call_fext_func(device, + 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) return result; } @@ -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) { - int state = 0; + struct fujitsu_laptop *priv; int error; int i; - if (!device) - return -EINVAL; + priv = devm_kzalloc(&device->dev, sizeof(*priv), GFP_KERNEL); + 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; - sprintf(acpi_device_name(device), "%s", - ACPI_FUJITSU_LAPTOP_DEVICE_NAME); - sprintf(acpi_device_class(device), "%s", ACPI_FUJITSU_CLASS); - device->driver_data = fujitsu_laptop; + strcpy(acpi_device_name(device), ACPI_FUJITSU_LAPTOP_DEVICE_NAME); + strcpy(acpi_device_class(device), ACPI_FUJITSU_CLASS); + device->driver_data = priv; /* kfifo */ - spin_lock_init(&fujitsu_laptop->fifo_lock); - error = kfifo_alloc(&fujitsu_laptop->fifo, RINGBUFFERSIZE * sizeof(int), - GFP_KERNEL); + spin_lock_init(&priv->fifo_lock); + error = kfifo_alloc(&priv->fifo, RINGBUFFERSIZE * sizeof(int), + GFP_KERNEL); if (error) { pr_err("kfifo_alloc failed\n"); goto err_stop; @@ -795,51 +782,36 @@ static int acpi_fujitsu_laptop_add(struct acpi_device *device) if (error) goto err_free_fifo; - error = acpi_bus_update_power(fujitsu_laptop->acpi_handle, &state); - if (error) { - 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"); - } + pr_info("ACPI: %s [%s]\n", + acpi_device_name(device), acpi_device_bid(device)); 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) ; /* 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 = - call_fext_func(FUNC_FLAGS, 0x0, 0x0, 0x0); + priv->flags_supported = call_fext_func(device, FUNC_FLAGS, 0x0, 0x0, + 0x0); /* Make sure our bitmask of supported functions is cleared if the RFKILL function block is not implemented, like on the S7020. */ - if (fujitsu_laptop->flags_supported == UNSUPPORTED_CMD) - fujitsu_laptop->flags_supported = 0; + if (priv->flags_supported == UNSUPPORTED_CMD) + priv->flags_supported = 0; - if (fujitsu_laptop->flags_supported) - fujitsu_laptop->flags_state = - call_fext_func(FUNC_FLAGS, 0x4, 0x0, 0x0); + if (priv->flags_supported) + priv->flags_state = call_fext_func(device, FUNC_FLAGS, 0x4, 0x0, + 0x0); /* 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 */ - if (fujitsu_bl->bl_device && + if (fujitsu_bl && fujitsu_bl->bl_device && 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; else fujitsu_bl->bl_device->props.power = FB_BLANK_UNBLANK; @@ -849,103 +821,100 @@ static int acpi_fujitsu_laptop_add(struct acpi_device *device) if (error) goto err_free_fifo; - error = fujitsu_laptop_platform_add(); + error = fujitsu_laptop_platform_add(device); if (error) goto err_free_fifo; return 0; err_free_fifo: - kfifo_free(&fujitsu_laptop->fifo); + kfifo_free(&priv->fifo); err_stop: return error; } 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; } -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; - status = kfifo_in_locked(&fujitsu_laptop->fifo, - (unsigned char *)&scancode, sizeof(scancode), - &fujitsu_laptop->fifo_lock); + status = kfifo_in_locked(&priv->fifo, (unsigned char *)&scancode, + sizeof(scancode), &priv->fifo_lock); if (status != sizeof(scancode)) { - vdbg_printk(FUJLAPTOP_DBG_WARN, - "Could not push scancode [0x%x]\n", scancode); + dev_info(&priv->input->dev, "Could not push scancode [0x%x]\n", + scancode); return; } - sparse_keymap_report_event(input, scancode, 1, false); - vdbg_printk(FUJLAPTOP_DBG_TRACE, - "Push scancode into ringbuffer [0x%x]\n", scancode); + sparse_keymap_report_event(priv->input, scancode, 1, false); + dev_dbg(&priv->input->dev, "Push scancode into ringbuffer [0x%x]\n", + 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; while (true) { - status = kfifo_out_locked(&fujitsu_laptop->fifo, + status = kfifo_out_locked(&priv->fifo, (unsigned char *)&scancode, - sizeof(scancode), - &fujitsu_laptop->fifo_lock); + sizeof(scancode), &priv->fifo_lock); if (status != sizeof(scancode)) return; - sparse_keymap_report_event(input, scancode, 0, false); - vdbg_printk(FUJLAPTOP_DBG_TRACE, - "Pop scancode from ringbuffer [0x%x]\n", scancode); + sparse_keymap_report_event(priv->input, scancode, 0, false); + dev_dbg(&priv->input->dev, + "Pop scancode from ringbuffer [0x%x]\n", scancode); } } 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; unsigned int irb; - input = fujitsu_laptop->input; - if (event != ACPI_FUJITSU_NOTIFY_CODE1) { - vdbg_printk(FUJLAPTOP_DBG_WARN, - "Unsupported event [0x%x]\n", event); - sparse_keymap_report_event(input, -1, 1, true); + acpi_handle_info(device->handle, "Unsupported event [0x%x]\n", + event); + sparse_keymap_report_event(priv->input, -1, 1, true); return; } - if (fujitsu_laptop->flags_supported) - fujitsu_laptop->flags_state = - call_fext_func(FUNC_FLAGS, 0x4, 0x0, 0x0); + if (priv->flags_supported) + priv->flags_state = call_fext_func(device, FUNC_FLAGS, 0x4, 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) { scancode = irb & 0x4ff; - if (sparse_keymap_entry_from_scancode(input, scancode)) - acpi_fujitsu_laptop_press(scancode); + if (sparse_keymap_entry_from_scancode(priv->input, scancode)) + acpi_fujitsu_laptop_press(device, scancode); else if (scancode == 0) - acpi_fujitsu_laptop_release(); + acpi_fujitsu_laptop_release(device); else - vdbg_printk(FUJLAPTOP_DBG_WARN, - "Unknown GIRB result [%x]\n", irb); + acpi_handle_info(device->handle, + "Unknown GIRB result [%x]\n", irb); } /* On some models (first seen on the Skylake-based Lifebook * E736/E746/E756), the touchpad toggle hotkey (Fn+F4) is * handled in software; its state is queried using FUNC_FLAGS */ - if ((fujitsu_laptop->flags_supported & BIT(26)) && - (call_fext_func(FUNC_FLAGS, 0x1, 0x0, 0x0) & BIT(26))) - sparse_keymap_report_event(input, BIT(26), 1, true); + if ((priv->flags_supported & BIT(26)) && + (call_fext_func(device, FUNC_FLAGS, 0x1, 0x0, 0x0) & BIT(26))) + sparse_keymap_report_event(priv->input, BIT(26), 1, true); } /* Initialization */ @@ -992,16 +961,9 @@ static int __init fujitsu_init(void) { 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); if (ret) - goto err_free_fujitsu_bl; + return ret; /* Register platform stuff */ @@ -1011,28 +973,18 @@ static int __init fujitsu_init(void) /* 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); if (ret) - goto err_free_fujitsu_laptop; + goto err_unregister_platform_driver; pr_info("driver " FUJITSU_DRIVER_VERSION " successfully loaded\n"); return 0; -err_free_fujitsu_laptop: - kfree(fujitsu_laptop); err_unregister_platform_driver: platform_driver_unregister(&fujitsu_pf_driver); err_unregister_acpi: acpi_bus_unregister_driver(&acpi_fujitsu_bl_driver); -err_free_fujitsu_bl: - kfree(fujitsu_bl); return ret; } @@ -1041,14 +993,10 @@ static void __exit fujitsu_cleanup(void) { acpi_bus_unregister_driver(&acpi_fujitsu_laptop_driver); - kfree(fujitsu_laptop); - platform_driver_unregister(&fujitsu_pf_driver); acpi_bus_unregister_driver(&acpi_fujitsu_bl_driver); - kfree(fujitsu_bl); - pr_info("driver unloaded\n"); } @@ -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_param(disable_brightness_adjust, bool, 0644); 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_DESCRIPTION("Fujitsu laptop extras support"); diff --git a/drivers/platform/x86/ideapad-laptop.c b/drivers/platform/x86/ideapad-laptop.c index 24ca9fbe31cc..527e5d9ab9bf 100644 --- a/drivers/platform/x86/ideapad-laptop.c +++ b/drivers/platform/x86/ideapad-laptop.c @@ -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 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[] = { &dev_attr_camera_power.attr, &dev_attr_fan_mode.attr, + &dev_attr_touchpad.attr, NULL }; @@ -478,7 +512,7 @@ static int ideapad_rfk_set(void *data, bool 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, }; @@ -810,7 +844,6 @@ static void ideapad_acpi_notify(acpi_handle handle, u32 event, void *data) case 8: case 7: case 6: - case 1: ideapad_input_report(priv, vpc_bit); break; case 5: @@ -828,6 +861,13 @@ static void ideapad_acpi_notify(acpi_handle handle, u32 event, void *data) case 0: ideapad_check_special_buttons(priv); 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: pr_info("Unknown event: %lu\n", vpc_bit); } @@ -911,6 +951,20 @@ static const struct dmi_system_id no_hw_rfkill_list[] = { }, }, { + .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", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), diff --git a/drivers/platform/x86/intel_cht_int33fe.c b/drivers/platform/x86/intel_cht_int33fe.c index 6a1b2ca5b6fe..da706e2c4232 100644 --- a/drivers/platform/x86/intel_cht_int33fe.c +++ b/drivers/platform/x86/intel_cht_int33fe.c @@ -34,6 +34,13 @@ struct cht_int33fe_data { 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) { struct device *dev = &client->dev; @@ -70,6 +77,7 @@ static int cht_int33fe_probe(struct i2c_client *client) memset(&board_info, 0, sizeof(board_info)); strlcpy(board_info.type, "max17047", I2C_NAME_SIZE); + board_info.properties = max17047_props; data->max17047 = i2c_acpi_new_device(dev, 1, &board_info); if (!data->max17047) diff --git a/drivers/platform/x86/intel_menlow.c b/drivers/platform/x86/intel_menlow.c index cbe01021c939..ef9b0af8cdd3 100644 --- a/drivers/platform/x86/intel_menlow.c +++ b/drivers/platform/x86/intel_menlow.c @@ -142,7 +142,7 @@ static int memory_set_cur_bandwidth(struct thermal_cooling_device *cdev, 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_cur_state = memory_get_cur_bandwidth, .set_cur_state = memory_set_cur_bandwidth, diff --git a/drivers/platform/x86/intel_pmc_ipc.c b/drivers/platform/x86/intel_pmc_ipc.c index e4d4dfe3e1d1..bb792a52248b 100644 --- a/drivers/platform/x86/intel_pmc_ipc.c +++ b/drivers/platform/x86/intel_pmc_ipc.c @@ -186,7 +186,7 @@ static inline void ipc_data_writel(u32 data, u32 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); } diff --git a/drivers/platform/x86/msi-laptop.c b/drivers/platform/x86/msi-laptop.c index 9e90827c176a..61b9014d2610 100644 --- a/drivers/platform/x86/msi-laptop.c +++ b/drivers/platform/x86/msi-laptop.c @@ -563,11 +563,11 @@ static struct attribute *msipf_old_attributes[] = { NULL }; -static struct attribute_group msipf_attribute_group = { +static const struct attribute_group msipf_attribute_group = { .attrs = msipf_attributes }; -static struct attribute_group msipf_old_attribute_group = { +static const struct attribute_group msipf_old_attribute_group = { .attrs = msipf_old_attributes }; diff --git a/drivers/platform/x86/panasonic-laptop.c b/drivers/platform/x86/panasonic-laptop.c index 975f4e100dbd..76b0a58e205b 100644 --- a/drivers/platform/x86/panasonic-laptop.c +++ b/drivers/platform/x86/panasonic-laptop.c @@ -228,10 +228,6 @@ struct pcc_acpi { struct backlight_device *backlight; }; -struct pcc_keyinput { - struct acpi_hotkey *hotkey; -}; - /* method access functions */ static int acpi_pcc_write_sset(struct pcc_acpi *pcc, int func, int val) { diff --git a/drivers/platform/x86/peaq-wmi.c b/drivers/platform/x86/peaq-wmi.c new file mode 100644 index 000000000000..ca75b4dc437e --- /dev/null +++ b/drivers/platform/x86/peaq-wmi.c @@ -0,0 +1,100 @@ +/* + * 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"); diff --git a/drivers/platform/x86/samsung-laptop.c b/drivers/platform/x86/samsung-laptop.c index 8c146e2b6727..5c4dfe48f03d 100644 --- a/drivers/platform/x86/samsung-laptop.c +++ b/drivers/platform/x86/samsung-laptop.c @@ -591,7 +591,7 @@ static int seclinux_rfkill_set(void *data, bool blocked) !blocked); } -static struct rfkill_ops seclinux_rfkill_ops = { +static const struct rfkill_ops seclinux_rfkill_ops = { .set_block = seclinux_rfkill_set, }; @@ -651,7 +651,7 @@ static void swsmi_rfkill_query(struct rfkill *rfkill, void *priv) 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, .query = swsmi_rfkill_query, }; @@ -1446,9 +1446,9 @@ static int __init samsung_sabi_init(struct samsung_laptop *samsung) const struct sabi_config *config = NULL; const struct sabi_commands *commands; unsigned int ifaceP; + int loca = 0xffff; int ret = 0; int i; - int loca; samsung->f0000_segment = ioremap_nocache(0xf0000, 0xffff); if (!samsung->f0000_segment) { diff --git a/drivers/platform/x86/silead_dmi.c b/drivers/platform/x86/silead_dmi.c index a3a57d93cf06..3cd3bdfe51df 100644 --- a/drivers/platform/x86/silead_dmi.c +++ b/drivers/platform/x86/silead_dmi.c @@ -80,6 +80,48 @@ static const struct silead_ts_dmi_data surftab_wintron70_st70416_6_data = { .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[] = { { /* CUBE iwork8 Air */ @@ -117,6 +159,34 @@ static const struct dmi_system_id silead_ts_dmi_table[] = { 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"), + }, + }, { }, }; diff --git a/drivers/platform/x86/sony-laptop.c b/drivers/platform/x86/sony-laptop.c index aa2ee51d3547..bfae79534f44 100644 --- a/drivers/platform/x86/sony-laptop.c +++ b/drivers/platform/x86/sony-laptop.c @@ -222,7 +222,7 @@ struct sony_laptop_keypress { /* Correspondance table between sonypi events * 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, /* 1 SONYPI_EVENT_JOGDIAL_DOWN */ -1, /* 2 SONYPI_EVENT_JOGDIAL_UP */ @@ -4032,7 +4032,7 @@ static struct attribute *spic_attributes[] = { NULL }; -static struct attribute_group spic_attribute_group = { +static const struct attribute_group spic_attribute_group = { .attrs = spic_attributes }; diff --git a/drivers/platform/x86/thinkpad_acpi.c b/drivers/platform/x86/thinkpad_acpi.c index f6861b551178..b22573131e53 100644 --- a/drivers/platform/x86/thinkpad_acpi.c +++ b/drivers/platform/x86/thinkpad_acpi.c @@ -590,8 +590,8 @@ static int acpi_evalf(acpi_handle handle, break; /* add more types as needed */ default: - pr_err("acpi_evalf() called " - "with invalid format character '%c'\n", c); + pr_err("acpi_evalf() called with invalid format character '%c'\n", + c); va_end(ap); return 0; } @@ -619,8 +619,8 @@ static int acpi_evalf(acpi_handle handle, break; /* add more types as needed */ default: - pr_err("acpi_evalf() called " - "with invalid format character '%c'\n", res_type); + pr_err("acpi_evalf() called with invalid format character '%c'\n", + res_type); return 0; } @@ -790,8 +790,8 @@ static int __init setup_acpi_notify(struct ibm_struct *ibm) ibm->acpi->type, dispatch_acpi_notify, ibm); if (ACPI_FAILURE(status)) { if (status == AE_ALREADY_EXISTS) { - pr_notice("another device driver is already " - "handling %s events\n", ibm->name); + pr_notice("another device driver is already handling %s events\n", + ibm->name); } else { pr_err("acpi_install_notify_handler(%s) failed: %s\n", ibm->name, acpi_format_exception(status)); @@ -1095,8 +1095,7 @@ static void printk_deprecated_attribute(const char * const what, const char * const details) { tpacpi_log_usertask("deprecated sysfs attribute"); - pr_warn("WARNING: sysfs attribute %s is deprecated and " - "will be removed. %s\n", + pr_warn("WARNING: sysfs attribute %s is deprecated and will be removed. %s\n", what, details); } @@ -1796,8 +1795,7 @@ static void __init tpacpi_check_outdated_fw(void) * best if the user upgrades the firmware anyway. */ pr_warn("WARNING: Outdated ThinkPad BIOS/EC firmware\n"); - pr_warn("WARNING: This firmware may be missing critical bug " - "fixes and/or important features\n"); + pr_warn("WARNING: This firmware may be missing critical bug fixes and/or important features\n"); } } @@ -2166,8 +2164,7 @@ static int hotkey_mask_set(u32 mask) * a given event. */ if (!hotkey_mask_get() && !rc && (fwmask & ~hotkey_acpi_mask)) { - pr_notice("asked for hotkey mask 0x%08x, but " - "firmware forced it to 0x%08x\n", + pr_notice("asked for hotkey mask 0x%08x, but firmware forced it to 0x%08x\n", fwmask, hotkey_acpi_mask); } @@ -2192,11 +2189,9 @@ static int hotkey_user_mask_set(const u32 mask) (mask == 0xffff || mask == 0xffffff || mask == 0xffffffff)) { tp_warned.hotkey_mask_ff = 1; - pr_notice("setting the hotkey mask to 0x%08x is likely " - "not the best way to go about it\n", mask); - pr_notice("please consider using the driver defaults, " - "and refer to up-to-date thinkpad-acpi " - "documentation\n"); + pr_notice("setting the hotkey mask to 0x%08x is likely not the best way to go about it\n", + mask); + pr_notice("please consider using the driver defaults, and refer to up-to-date thinkpad-acpi documentation\n"); } /* 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) NULL, TPACPI_NVRAM_KTHREAD_NAME); if (IS_ERR(tpacpi_hotkey_task)) { tpacpi_hotkey_task = NULL; - pr_err("could not create kernel thread " - "for hotkey polling\n"); + pr_err("could not create kernel thread for hotkey polling\n"); } } } else { hotkey_poll_stop_sync(); if (may_warn && (poll_driver_mask || poll_user_mask) && hotkey_poll_freq == 0) { - pr_notice("hot keys 0x%08x and/or events 0x%08x " - "require polling, which is currently " - "disabled\n", + pr_notice("hot keys 0x%08x and/or events 0x%08x require polling, which is currently disabled\n", poll_user_mask, poll_driver_mask); } } @@ -2808,12 +2800,10 @@ static ssize_t hotkey_source_mask_store(struct device *dev, mutex_unlock(&hotkey_mutex); if (rc < 0) - pr_err("hotkey_source_mask: " - "failed to update the firmware event mask!\n"); + pr_err("hotkey_source_mask: failed to update the firmware event mask!\n"); if (r_ev) - pr_notice("hotkey_source_mask: " - "some important events were disabled: 0x%04x\n", + pr_notice("hotkey_source_mask: some important events were disabled: 0x%04x\n", r_ev); tpacpi_disclose_usertask("hotkey_source_mask", "set to 0x%08lx\n", t); @@ -3074,8 +3064,7 @@ static void hotkey_exit(void) if (((tp_features.hotkey_mask && hotkey_mask_set(hotkey_orig_mask)) | hotkey_status_set(false)) != 0) - pr_err("failed to restore hot key mask " - "to BIOS defaults\n"); + pr_err("failed to restore hot key mask to BIOS defaults\n"); } static void __init hotkey_unmap(const unsigned int scancode) @@ -3587,11 +3576,8 @@ static int __init hotkey_init(struct ibm_init_struct *iibm) * userspace. tpacpi_detect_brightness_capabilities() must have * been called before this point */ if (acpi_video_get_backlight_type() != acpi_backlight_vendor) { - pr_info("This ThinkPad has standard ACPI backlight " - "brightness control, supported by the ACPI " - "video driver\n"); - pr_notice("Disabling thinkpad-acpi brightness events " - "by default...\n"); + pr_info("This ThinkPad has standard ACPI backlight brightness control, supported by the ACPI video driver\n"); + pr_notice("Disabling thinkpad-acpi brightness events by default...\n"); /* Disable brightness up/down on Lenovo thinkpads when * ACPI is handling them, otherwise it is plain impossible @@ -3760,7 +3746,7 @@ static bool adaptive_keyboard_hotkey_notify_hotkey(unsigned int scancode) TP_ACPI_HOTKEYSCAN_EXTENDED_START - TP_ACPI_HOTKEYSCAN_ADAPTIVE_START) { pr_info("Unhandled adaptive keyboard key: 0x%x\n", - scancode); + scancode); return false; } keycode = hotkey_keycode_map[scancode - FIRST_ADAPTIVE_KEY + @@ -3957,14 +3943,12 @@ static bool hotkey_notify_6xxx(const u32 hkey, /* recommended action: immediate sleep/hibernate */ break; case TP_HKEY_EV_ALARM_SENSOR_HOT: - pr_crit("THERMAL ALARM: " - "a sensor reports something is too hot!\n"); + pr_crit("THERMAL ALARM: a sensor reports something is too hot!\n"); /* recommended action: warn user through gui, that */ /* some internal component is too hot */ break; case TP_HKEY_EV_ALARM_SENSOR_XHOT: - pr_alert("THERMAL EMERGENCY: " - "a sensor reports something is extremely hot!\n"); + pr_alert("THERMAL EMERGENCY: a sensor reports something is extremely hot!\n"); /* recommended action: immediate sleep/hibernate */ break; case TP_HKEY_EV_AC_CHANGED: @@ -4089,8 +4073,8 @@ static void hotkey_notify(struct ibm_struct *ibm, u32 event) } if (!known_ev) { pr_notice("unhandled HKEY event 0x%04x\n", hkey); - pr_notice("please report the conditions when this " - "event happened to %s\n", TPACPI_MAIL); + pr_notice("please report the conditions when this event happened to %s\n", + TPACPI_MAIL); } /* netlink events */ @@ -4124,8 +4108,7 @@ static void hotkey_resume(void) if (hotkey_status_set(true) < 0 || hotkey_mask_set(hotkey_acpi_mask) < 0) - pr_err("error while attempting to reset the event " - "firmware interface\n"); + pr_err("error while attempting to reset the event firmware interface\n"); tpacpi_send_radiosw_update(); hotkey_tablet_mode_notify_change(); @@ -4177,12 +4160,8 @@ static void hotkey_enabledisable_warn(bool enable) { tpacpi_log_usertask("procfs hotkey enable/disable"); if (!WARN((tpacpi_lifecycle == TPACPI_LIFE_RUNNING || !enable), - pr_fmt("hotkey enable/disable functionality has been " - "removed from the driver. " - "Hotkeys are always enabled.\n"))) - pr_err("Please remove the hotkey=enable module " - "parameter, it is deprecated. " - "Hotkeys are always enabled.\n"); + pr_fmt("hotkey enable/disable functionality has been removed from the driver. 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) @@ -4840,8 +4819,7 @@ static void video_exit(void) dbg_printk(TPACPI_DBG_EXIT, "restoring original video autoswitch mode\n"); if (video_autosw_set(video_orig_autosw)) - pr_err("error while trying to restore original " - "video autoswitch mode\n"); + pr_err("error while trying to restore original video autoswitch mode\n"); } static int video_outputsw_get(void) @@ -5931,8 +5909,7 @@ static int __init led_init(struct ibm_init_struct *iibm) } #ifdef CONFIG_THINKPAD_ACPI_UNSAFE_LEDS - pr_notice("warning: userspace override of important " - "firmware LEDs is enabled\n"); + pr_notice("warning: userspace override of important firmware LEDs is enabled\n"); #endif return 0; } @@ -5961,8 +5938,7 @@ static int led_read(struct seq_file *m) } } - seq_printf(m, "commands:\t" - "<led> on, <led> off, <led> blink (<led> is 0-15)\n"); + seq_printf(m, "commands:\t<led> on, <led> off, <led> blink (<led> is 0-15)\n"); return 0; } @@ -6335,13 +6311,10 @@ static int __init thermal_init(struct ibm_init_struct *iibm) if (ta1 == 0) { /* This is sheer paranoia, but we handle it anyway */ if (acpi_tmp7) { - pr_err("ThinkPad ACPI EC access misbehaving, " - "falling back to ACPI TMPx access " - "mode\n"); + pr_err("ThinkPad ACPI EC access misbehaving, falling back to ACPI TMPx access mode\n"); thermal_read_mode = TPACPI_THERMAL_ACPI_TMP07; } else { - pr_err("ThinkPad ACPI EC access misbehaving, " - "disabling thermal sensors access\n"); + pr_err("ThinkPad ACPI EC access misbehaving, disabling thermal sensors access\n"); thermal_read_mode = TPACPI_THERMAL_NONE; } } else { @@ -6820,26 +6793,20 @@ static int __init brightness_init(struct ibm_init_struct *iibm) if (!brightness_enable) { dbg_printk(TPACPI_DBG_INIT | TPACPI_DBG_BRGHT, - "brightness support disabled by " - "module parameter\n"); + "brightness support disabled by module parameter\n"); return 1; } if (acpi_video_get_backlight_type() != acpi_backlight_vendor) { if (brightness_enable > 1) { - pr_info("Standard ACPI backlight interface " - "available, not loading native one\n"); + pr_info("Standard ACPI backlight interface available, not loading native one\n"); return 1; } else if (brightness_enable == 1) { - pr_warn("Cannot enable backlight brightness support, " - "ACPI is already handling it. Refer to the " - "acpi_backlight kernel parameter.\n"); + pr_warn("Cannot enable backlight brightness support, ACPI is already handling it. Refer to the acpi_backlight kernel parameter.\n"); return 1; } } else if (tp_features.bright_acpimode && brightness_enable > 1) { - pr_notice("Standard ACPI backlight interface not " - "available, thinkpad_acpi native " - "brightness control enabled\n"); + pr_notice("Standard ACPI backlight interface not available, thinkpad_acpi native brightness control enabled\n"); } /* @@ -6890,10 +6857,10 @@ static int __init brightness_init(struct ibm_init_struct *iibm) "brightness is supported\n"); if (quirks & TPACPI_BRGHT_Q_ASK) { - pr_notice("brightness: will use unverified default: " - "brightness_mode=%d\n", brightness_mode); - pr_notice("brightness: please report to %s whether it works well " - "or not on your ThinkPad\n", TPACPI_MAIL); + pr_notice("brightness: will use unverified default: brightness_mode=%d\n", + brightness_mode); + pr_notice("brightness: please report to %s whether it works well or not on your ThinkPad\n", + TPACPI_MAIL); } /* 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) backlight_update_status(ibm_backlight_device); vdbg_printk(TPACPI_DBG_INIT | TPACPI_DBG_BRGHT, - "brightness: registering brightness hotkeys " - "as change notification\n"); + "brightness: registering brightness hotkeys as change notification\n"); tpacpi_hotkey_driver_mask_set(hotkey_driver_mask | TP_ACPI_HKEY_BRGHTUP_MASK | TP_ACPI_HKEY_BRGHTDWN_MASK); @@ -7567,8 +7533,8 @@ static int __init volume_init(struct ibm_init_struct *iibm) return -EINVAL; if (volume_mode == TPACPI_VOL_MODE_UCMS_STEP) { - pr_err("UCMS step volume mode not implemented, " - "please contact %s\n", TPACPI_MAIL); + pr_err("UCMS step volume mode not implemented, please contact %s\n", + TPACPI_MAIL); return 1; } @@ -7581,8 +7547,7 @@ static int __init volume_init(struct ibm_init_struct *iibm) */ if (!alsa_enable) { vdbg_printk(TPACPI_DBG_INIT | TPACPI_DBG_MIXER, - "ALSA mixer disabled by parameter, " - "not loading volume subdriver...\n"); + "ALSA mixer disabled by parameter, not loading volume subdriver...\n"); return 1; } @@ -7674,12 +7639,9 @@ static int volume_read(struct seq_file *m) if (volume_control_allowed) { seq_printf(m, "commands:\tunmute, mute\n"); if (!tp_features.mixer_no_level_control) { - seq_printf(m, - "commands:\tup, down\n"); - seq_printf(m, - "commands:\tlevel <level>" - " (<level> is 0-%d)\n", - TP_EC_VOLUME_MAX); + seq_printf(m, "commands:\tup, down\n"); + seq_printf(m, "commands:\tlevel <level> (<level> is 0-%d)\n", + TP_EC_VOLUME_MAX); } } } @@ -7702,10 +7664,8 @@ static int volume_write(char *buf) if (!volume_control_allowed && tpacpi_lifecycle != TPACPI_LIFE_INIT) { if (unlikely(!tp_warned.volume_ctrl_forbidden)) { tp_warned.volume_ctrl_forbidden = 1; - pr_notice("Console audio control in monitor mode, " - "changes are not allowed\n"); - pr_notice("Use the volume_control=1 module parameter " - "to enable volume control\n"); + pr_notice("Console audio control in monitor mode, changes are not allowed\n"); + pr_notice("Use the volume_control=1 module parameter to enable volume control\n"); } return -EPERM; } @@ -7987,8 +7947,7 @@ TPACPI_HANDLE(sfan, ec, "SFAN", /* 570 */ static void fan_quirk1_setup(void) { if (fan_control_initial_status == 0x07) { - pr_notice("fan_init: initial fan status is unknown, " - "assuming it is in auto mode\n"); + pr_notice("fan_init: initial fan status is unknown, assuming it is in auto mode\n"); tp_features.fan_ctrl_status_undef = 1; } } @@ -8385,8 +8344,8 @@ static void fan_watchdog_fire(struct work_struct *ignored) pr_notice("fan watchdog: enabling fan\n"); rc = fan_set_enable(); if (rc < 0) { - pr_err("fan watchdog: error %d while enabling fan, " - "will try again later...\n", -rc); + pr_err("fan watchdog: error %d while enabling fan, will try again later...\n", + rc); /* reschedule for later */ fan_watchdog_reset(); } @@ -8680,8 +8639,7 @@ static int __init fan_init(struct ibm_init_struct *iibm) "secondary fan support enabled\n"); } } else { - pr_err("ThinkPad ACPI EC access misbehaving, " - "fan status and control unavailable\n"); + pr_err("ThinkPad ACPI EC access misbehaving, fan status and control unavailable\n"); return 1; } } @@ -8780,8 +8738,8 @@ static void fan_suspend(void) fan_control_resume_level = 0; rc = fan_get_status_safe(&fan_control_resume_level); if (rc < 0) - pr_notice("failed to read fan level for later " - "restore during resume: %d\n", rc); + pr_notice("failed to read fan level for later restore during resume: %d\n", + rc); /* if it is undefined, don't attempt to restore it. * KEEP THIS LAST */ @@ -8900,20 +8858,17 @@ static int fan_read(struct seq_file *m) break; default: - seq_printf(m, " (<level> is 0-7, " - "auto, disengaged, full-speed)\n"); + seq_printf(m, " (<level> is 0-7, auto, disengaged, full-speed)\n"); break; } } if (fan_control_commands & TPACPI_FAN_CMD_ENABLE) seq_printf(m, "commands:\tenable, disable\n" - "commands:\twatchdog <timeout> (<timeout> " - "is 0 (off), 1-120 (seconds))\n"); + "commands:\twatchdog <timeout> (<timeout> is 0 (off), 1-120 (seconds))\n"); if (fan_control_commands & TPACPI_FAN_CMD_SPEED) - seq_printf(m, "commands:\tspeed <speed>" - " (<speed> is 0-65535)\n"); + seq_printf(m, "commands:\tspeed <speed> (<speed> is 0-65535)\n"); return 0; } @@ -9439,8 +9394,7 @@ static int __must_check __init get_thinkpad_model_data( tp->ec_release = (ec_fw_string[4] << 8) | ec_fw_string[5]; } else { - pr_notice("ThinkPad firmware release %s " - "doesn't match the known patterns\n", + pr_notice("ThinkPad firmware release %s doesn't match the known patterns\n", ec_fw_string); pr_notice("please report this to %s\n", TPACPI_MAIL); @@ -9635,8 +9589,7 @@ MODULE_PARM_DESC(debug, "Sets debug level bit-mask"); module_param(force_load, bool, 0444); MODULE_PARM_DESC(force_load, - "Attempts to load the driver even on a " - "mis-identified ThinkPad when true"); + "Attempts to load the driver even on a mis-identified ThinkPad when true"); module_param_named(fan_control, fan_control_allowed, bool, 0444); MODULE_PARM_DESC(fan_control, @@ -9644,8 +9597,7 @@ MODULE_PARM_DESC(fan_control, module_param_named(brightness_mode, brightness_mode, uint, 0444); MODULE_PARM_DESC(brightness_mode, - "Selects brightness control strategy: " - "0=auto, 1=EC, 2=UCMS, 3=EC+NVRAM"); + "Selects brightness control strategy: 0=auto, 1=EC, 2=UCMS, 3=EC+NVRAM"); module_param(brightness_enable, uint, 0444); MODULE_PARM_DESC(brightness_enable, @@ -9654,18 +9606,15 @@ MODULE_PARM_DESC(brightness_enable, #ifdef CONFIG_THINKPAD_ACPI_ALSA_SUPPORT module_param_named(volume_mode, volume_mode, uint, 0444); MODULE_PARM_DESC(volume_mode, - "Selects volume control strategy: " - "0=auto, 1=EC, 2=N/A, 3=EC+NVRAM"); + "Selects volume control strategy: 0=auto, 1=EC, 2=N/A, 3=EC+NVRAM"); module_param_named(volume_capabilities, volume_capabilities, uint, 0444); MODULE_PARM_DESC(volume_capabilities, - "Selects the mixer capabilites: " - "0=auto, 1=volume and mute, 2=mute only"); + "Selects the mixer capabilites: 0=auto, 1=volume and mute, 2=mute only"); module_param_named(volume_control, volume_control_allowed, bool, 0444); MODULE_PARM_DESC(volume_control, - "Enables software override for the console audio " - "control when true"); + "Enables software override for the console audio control when true"); module_param_named(software_mute, software_mute_requested, bool, 0444); MODULE_PARM_DESC(software_mute, @@ -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"); #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) \ module_param_call(feature, set_ibm_param, NULL, NULL, 0); \ - MODULE_PARM_DESC(feature, "Simulates thinkpad-acpi procfs command " \ - "at module load, see documentation") + MODULE_PARM_DESC(feature, "Simulates thinkpad-acpi procfs command at module load, see documentation") TPACPI_PARAM(hotkey); TPACPI_PARAM(bluetooth); diff --git a/drivers/platform/x86/topstar-laptop.c b/drivers/platform/x86/topstar-laptop.c index 70205d222da9..1032c00b907b 100644 --- a/drivers/platform/x86/topstar-laptop.c +++ b/drivers/platform/x86/topstar-laptop.c @@ -162,6 +162,7 @@ static int acpi_topstar_remove(struct acpi_device *device) } static const struct acpi_device_id topstar_device_ids[] = { + { "TPS0001", 0 }, { "TPSACPI01", 0 }, { "", 0 }, }; diff --git a/drivers/platform/x86/toshiba_acpi.c b/drivers/platform/x86/toshiba_acpi.c index d0daf75cbed1..88f9f79a7cf6 100644 --- a/drivers/platform/x86/toshiba_acpi.c +++ b/drivers/platform/x86/toshiba_acpi.c @@ -1502,14 +1502,9 @@ static ssize_t video_proc_write(struct file *file, const char __user *buf, int ret; u32 video_out; - cmd = kmalloc(count + 1, GFP_KERNEL); - if (!cmd) - return -ENOMEM; - if (copy_from_user(cmd, buf, count)) { - kfree(cmd); - return -EFAULT; - } - cmd[count] = '\0'; + cmd = memdup_user_nul(buf, count); + if (IS_ERR(cmd)) + return PTR_ERR(cmd); buffer = cmd; diff --git a/drivers/platform/x86/toshiba_haps.c b/drivers/platform/x86/toshiba_haps.c index b3dec521e2b6..fb2736602558 100644 --- a/drivers/platform/x86/toshiba_haps.c +++ b/drivers/platform/x86/toshiba_haps.c @@ -132,7 +132,7 @@ static struct attribute *haps_attributes[] = { NULL, }; -static struct attribute_group haps_attr_group = { +static const struct attribute_group haps_attr_group = { .attrs = haps_attributes, }; diff --git a/drivers/platform/x86/wmi-bmof.c b/drivers/platform/x86/wmi-bmof.c new file mode 100644 index 000000000000..c4530ba715e8 --- /dev/null +++ b/drivers/platform/x86/wmi-bmof.c @@ -0,0 +1,125 @@ +/* + * 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"); diff --git a/drivers/platform/x86/wmi.c b/drivers/platform/x86/wmi.c index ceeb8c188ef3..1a764e311e11 100644 --- a/drivers/platform/x86/wmi.c +++ b/drivers/platform/x86/wmi.c @@ -8,6 +8,10 @@ * Copyright (c) 2001-2007 Anton Altaparmakov * 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 @@ -37,6 +41,8 @@ #include <linux/acpi.h> #include <linux/slab.h> #include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/wmi.h> #include <linux/uuid.h> ACPI_MODULE_NAME("wmi"); @@ -44,8 +50,6 @@ MODULE_AUTHOR("Carlos Corbacho"); MODULE_DESCRIPTION("ACPI-WMI Mapping Driver"); MODULE_LICENSE("GPL"); -#define ACPI_WMI_CLASS "wmi" - static LIST_HEAD(wmi_block_list); struct guid_block { @@ -62,12 +66,14 @@ struct guid_block { }; struct wmi_block { + struct wmi_device dev; struct list_head list; struct guid_block gblock; - acpi_handle handle; + struct acpi_device *acpi_device; wmi_notify_handler handler; void *handler_data; - struct device dev; + + bool read_takes_no_args; }; @@ -90,9 +96,8 @@ module_param(debug_dump_wdg, bool, 0444); MODULE_PARM_DESC(debug_dump_wdg, "Dump available WMI interfaces [0/1]"); -static int acpi_wmi_remove(struct acpi_device *device); -static int acpi_wmi_add(struct acpi_device *device); -static void acpi_wmi_notify(struct acpi_device *device, u32 event); +static int acpi_wmi_remove(struct platform_device *device); +static int acpi_wmi_probe(struct platform_device *device); static const struct acpi_device_id wmi_device_ids[] = { {"PNP0C14", 0}, @@ -101,15 +106,13 @@ static const struct acpi_device_id wmi_device_ids[] = { }; MODULE_DEVICE_TABLE(acpi, wmi_device_ids); -static struct acpi_driver acpi_wmi_driver = { - .name = "wmi", - .class = ACPI_WMI_CLASS, - .ids = wmi_device_ids, - .ops = { - .add = acpi_wmi_add, - .remove = acpi_wmi_remove, - .notify = acpi_wmi_notify, +static struct platform_driver acpi_wmi_driver = { + .driver = { + .name = "acpi-wmi", + .acpi_match_table = wmi_device_ids, }, + .probe = acpi_wmi_probe, + .remove = acpi_wmi_remove, }; /* @@ -139,6 +142,30 @@ static bool find_guid(const char *guid_string, struct wmi_block **out) 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) { struct guid_block *block = NULL; @@ -147,7 +174,7 @@ static acpi_status wmi_method_enable(struct wmi_block *wblock, int enable) acpi_handle handle; block = &wblock->gblock; - handle = wblock->handle; + handle = wblock->acpi_device->handle; snprintf(method, 5, "WE%02X", block->notify_id); status = acpi_execute_simple_method(handle, method, enable); @@ -186,7 +213,7 @@ u32 method_id, const struct acpi_buffer *in, struct acpi_buffer *out) return AE_ERROR; block = &wblock->gblock; - handle = wblock->handle; + handle = wblock->acpi_device->handle; if (!(block->flags & ACPI_WMI_METHOD)) return AE_BAD_DATA; @@ -221,19 +248,10 @@ u32 method_id, const struct acpi_buffer *in, struct acpi_buffer *out) } EXPORT_SYMBOL_GPL(wmi_evaluate_method); -/** - * wmi_query_block - Return contents of a WMI block - * @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) +static acpi_status __query_block(struct wmi_block *wblock, u8 instance, + struct acpi_buffer *out) { struct guid_block *block = NULL; - struct wmi_block *wblock = NULL; acpi_handle handle; acpi_status status, wc_status = AE_ERROR; struct acpi_object_list input; @@ -241,14 +259,11 @@ struct acpi_buffer *out) char method[5]; char wc_method[5] = "WC"; - if (!guid_string || !out) + if (!out) return AE_BAD_PARAMETER; - if (!find_guid(guid_string, &wblock)) - return AE_ERROR; - block = &wblock->gblock; - handle = wblock->handle; + handle = wblock->acpi_device->handle; if (block->instance_count < instance) return AE_BAD_PARAMETER; @@ -262,6 +277,9 @@ struct acpi_buffer *out) wq_params[0].type = ACPI_TYPE_INTEGER; 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 * enable collection. @@ -294,8 +312,59 @@ struct acpi_buffer *out) 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); +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 * @guid_string: 36 char string of the form fa50ff2b-f2e8-45de-83fa-65417f2f49ba @@ -305,7 +374,7 @@ EXPORT_SYMBOL_GPL(wmi_query_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, -const struct acpi_buffer *in) + const struct acpi_buffer *in) { struct guid_block *block = NULL; struct wmi_block *wblock = NULL; @@ -321,7 +390,7 @@ const struct acpi_buffer *in) return AE_ERROR; block = &wblock->gblock; - handle = wblock->handle; + handle = wblock->acpi_device->handle; if (block->instance_count < instance) return AE_BAD_PARAMETER; @@ -352,9 +421,10 @@ EXPORT_SYMBOL_GPL(wmi_set_block); static void wmi_dump_wdg(const struct guid_block *g) { pr_info("%pUL:\n", g->guid); - pr_info("\tobject_id: %c%c\n", g->object_id[0], g->object_id[1]); - pr_info("\tnotify_id: %02X\n", g->notify_id); - pr_info("\treserved: %02X\n", g->reserved); + if (g->flags & ACPI_WMI_EVENT) + pr_info("\tnotify_id: 0x%02X\n", g->notify_id); + else + pr_info("\tobject_id: %2pE\n", g->object_id); pr_info("\tinstance_count: %d\n", g->instance_count); pr_info("\tflags: %#x", g->flags); if (g->flags) { @@ -525,8 +595,8 @@ acpi_status wmi_get_event_data(u32 event, struct acpi_buffer *out) if ((gblock->flags & ACPI_WMI_EVENT) && (gblock->notify_id == event)) - return acpi_evaluate_object(wblock->handle, "_WED", - &input, out); + return acpi_evaluate_object(wblock->acpi_device->handle, + "_WED", &input, out); } return AE_NOT_FOUND; @@ -545,99 +615,320 @@ bool wmi_has_guid(const char *guid_string) } 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 */ static ssize_t modalias_show(struct device *dev, struct device_attribute *attr, char *buf) { - struct wmi_block *wblock; - - wblock = dev_get_drvdata(dev); - if (!wblock) { - strcat(buf, "\n"); - return strlen(buf); - } + struct wmi_block *wblock = dev_to_wblock(dev); return sprintf(buf, "wmi:%pUL\n", wblock->gblock.guid); } 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[] = { &dev_attr_modalias.attr, + &dev_attr_guid.attr, + &dev_attr_instance_count.attr, + &dev_attr_expensive.attr, NULL, }; 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; - wblock = dev_get_drvdata(dev); - if (!wblock) + if (add_uevent_var(env, "WMI_GUID=%pUL", wblock->gblock.guid)) return -ENOMEM; - sprintf(guid_string, "%pUL", wblock->gblock.guid); + return 0; +} - strcpy(&env->buf[env->buflen - 1], "wmi:"); - memcpy(&env->buf[env->buflen - 1 + 4], guid_string, 36); - env->buflen += 40; +static void wmi_dev_release(struct device *dev) +{ + 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; } -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", - .dev_release = wmi_dev_free, - .dev_uevent = wmi_dev_uevent, .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, - struct wmi_block *wblock, acpi_handle handle) +static struct device_type wmi_type_method = { + .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; /* Delete devices for all the GUIDs */ list_for_each_entry_safe(wblock, next, &wmi_block_list, list) { - list_del(&wblock->list); - if (wblock->dev.class) - device_unregister(&wblock->dev); - else - kfree(wblock); + if (wblock->acpi_device == device) { + list_del(&wblock->list); + device_unregister(&wblock->dev.dev); + } } } -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; - list_for_each_entry(wblock, &wmi_block_list, list) - if (memcmp(wblock->gblock.guid, guid_string, 16) == 0) + list_for_each_entry(wblock, &wmi_block_list, list) { + 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 false; } @@ -645,17 +936,17 @@ static bool guid_already_parsed(const char *guid_string) /* * 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}; - union acpi_object *obj; const struct guid_block *gblock; - struct wmi_block *wblock; + struct wmi_block *wblock, *next; + union acpi_object *obj; acpi_status status; - int retval; + int retval = 0; u32 i, total; - status = acpi_evaluate_object(handle, "_WDG", NULL, &out); + status = acpi_evaluate_object(device->handle, "_WDG", NULL, &out); if (ACPI_FAILURE(status)) return -ENXIO; @@ -675,25 +966,28 @@ static int parse_wdg(acpi_handle handle) if (debug_dump_wdg) 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); - if (!wblock) - return -ENOMEM; + if (!wblock) { + retval = -ENOMEM; + break; + } - wblock->handle = handle; + wblock->acpi_device = device; wblock->gblock = 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(gblock[i].guid)) { - retval = wmi_create_device(&gblock[i], wblock, handle); - if (retval) { - wmi_free_devices(); - goto out_free_pointer; - } + retval = wmi_create_device(wmi_bus_dev, &gblock[i], wblock, device); + if (retval) { + kfree(wblock); + continue; } list_add_tail(&wblock->list, &wmi_block_list); @@ -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: kfree(out.pointer); - return retval; } @@ -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 wmi_block *wblock; struct list_head *p; + bool found_it = false; list_for_each(p, &wmi_block_list) { wblock = list_entry(p, struct wmi_block, list); block = &wblock->gblock; - if ((block->flags & ACPI_WMI_EVENT) && - (block->notify_id == event)) { - if (wblock->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( - device->pnp.device_class, dev_name(&device->dev), - event, 0); + if (wblock->acpi_device->handle == handle && + (block->flags & ACPI_WMI_EVENT) && + (block->notify_id == event)) + { + found_it = true; 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); - wmi_free_devices(); + wmi_free_devices(acpi_device); + device_unregister((struct device *)dev_get_drvdata(&device->dev)); 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; 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_wmi_ec_space_handler, NULL, NULL); if (ACPI_FAILURE(status)) { - pr_err("Error installing EC region handler\n"); + dev_err(&device->dev, "Error installing EC region handler\n"); 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) { - acpi_remove_address_space_handler(device->handle, - ACPI_ADR_SPACE_EC, - &acpi_wmi_ec_space_handler); pr_err("Failed to parse WDG method\n"); - return error; + goto err_remove_busdev; } 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) { @@ -825,27 +1236,36 @@ static int __init acpi_wmi_init(void) if (acpi_disabled) return -ENODEV; - error = class_register(&wmi_class); + error = class_register(&wmi_bus_class); if (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) { pr_err("Error loading mapper\n"); - class_unregister(&wmi_class); - return error; + goto err_unreg_bus; } - pr_info("Mapper loaded\n"); 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) { - acpi_bus_unregister_driver(&acpi_wmi_driver); - class_unregister(&wmi_class); - - pr_info("Mapper unloaded\n"); + platform_driver_unregister(&acpi_wmi_driver); + class_unregister(&wmi_bus_class); + bus_unregister(&wmi_bus_type); } subsys_initcall(acpi_wmi_init); |