diff options
Diffstat (limited to 'drivers/platform/x86/thinkpad_acpi.c')
| -rw-r--r-- | drivers/platform/x86/thinkpad_acpi.c | 1943 |
1 files changed, 1124 insertions, 819 deletions
diff --git a/drivers/platform/x86/thinkpad_acpi.c b/drivers/platform/x86/thinkpad_acpi.c index e67e4feb35c..d82f196e3cf 100644 --- a/drivers/platform/x86/thinkpad_acpi.c +++ b/drivers/platform/x86/thinkpad_acpi.c @@ -21,7 +21,9 @@ * 02110-1301, USA. */ -#define TPACPI_VERSION "0.24" +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#define TPACPI_VERSION "0.25" #define TPACPI_SYSFS_VERSION 0x020700 /* @@ -58,7 +60,7 @@ #include <linux/kthread.h> #include <linux/freezer.h> #include <linux/delay.h> - +#include <linux/slab.h> #include <linux/nvram.h> #include <linux/proc_fs.h> #include <linux/seq_file.h> @@ -71,20 +73,16 @@ #include <linux/input.h> #include <linux/leds.h> #include <linux/rfkill.h> -#include <asm/uaccess.h> - #include <linux/dmi.h> #include <linux/jiffies.h> #include <linux/workqueue.h> - +#include <linux/acpi.h> +#include <linux/pci_ids.h> +#include <linux/thinkpad_acpi.h> #include <sound/core.h> #include <sound/control.h> #include <sound/initval.h> - -#include <acpi/acpi_drivers.h> - -#include <linux/pci_ids.h> - +#include <asm/uaccess.h> /* ThinkPad CMOS commands */ #define TP_CMOS_VOLUME_DOWN 0 @@ -121,8 +119,15 @@ enum { TP_NVRAM_POS_LEVEL_VOLUME = 0, }; +/* Misc NVRAM-related */ +enum { + TP_NVRAM_LEVEL_VOLUME_MAX = 14, +}; + /* ACPI HIDs */ -#define TPACPI_ACPI_HKEY_HID "IBM0068" +#define TPACPI_ACPI_IBM_HKEY_HID "IBM0068" +#define TPACPI_ACPI_LENOVO_HKEY_HID "LEN0068" +#define TPACPI_ACPI_EC_HID "PNP0C09" /* Input IDs */ #define TPACPI_HKEY_INPUT_PRODUCT 0x5054 /* "TP" */ @@ -174,6 +179,10 @@ enum tpacpi_hkey_event_t { /* Misc bay events */ TP_HKEY_EV_OPTDRV_EJ = 0x3006, /* opt. drive tray ejected */ + TP_HKEY_EV_HOTPLUG_DOCK = 0x4010, /* docked into hotplug dock + or port replicator */ + TP_HKEY_EV_HOTPLUG_UNDOCK = 0x4011, /* undocked from hotplug + dock or port replicator */ /* User-interface events */ TP_HKEY_EV_LID_CLOSE = 0x5001, /* laptop lid closed */ @@ -184,6 +193,10 @@ enum tpacpi_hkey_event_t { TP_HKEY_EV_PEN_REMOVED = 0x500c, /* tablet pen removed */ TP_HKEY_EV_BRGHT_CHANGED = 0x5010, /* backlight control event */ + /* Key-related user-interface events */ + TP_HKEY_EV_KEY_NUMLOCK = 0x6000, /* NumLock key pressed */ + TP_HKEY_EV_KEY_FN = 0x6005, /* Fn key pressed? E420 */ + /* Thermal events */ TP_HKEY_EV_ALARM_BAT_HOT = 0x6011, /* battery too hot */ TP_HKEY_EV_ALARM_BAT_XHOT = 0x6012, /* battery critically hot */ @@ -191,6 +204,9 @@ enum tpacpi_hkey_event_t { TP_HKEY_EV_ALARM_SENSOR_XHOT = 0x6022, /* sensor critically hot */ TP_HKEY_EV_THM_TABLE_CHANGED = 0x6030, /* thermal table changed */ + /* AC-related events */ + TP_HKEY_EV_AC_CHANGED = 0x6040, /* AC status changed */ + /* Misc */ TP_HKEY_EV_RFKILL_CHANGED = 0x7000, /* rfkill switch changed */ }; @@ -216,17 +232,6 @@ enum tpacpi_hkey_event_t { #define TPACPI_MAX_ACPI_ARGS 3 -/* printk headers */ -#define TPACPI_LOG TPACPI_FILE ": " -#define TPACPI_EMERG KERN_EMERG TPACPI_LOG -#define TPACPI_ALERT KERN_ALERT TPACPI_LOG -#define TPACPI_CRIT KERN_CRIT TPACPI_LOG -#define TPACPI_ERR KERN_ERR TPACPI_LOG -#define TPACPI_WARN KERN_WARNING TPACPI_LOG -#define TPACPI_NOTICE KERN_NOTICE TPACPI_LOG -#define TPACPI_INFO KERN_INFO TPACPI_LOG -#define TPACPI_DEBUG KERN_DEBUG TPACPI_LOG - /* Debugging printk groups */ #define TPACPI_DBG_ALL 0xffff #define TPACPI_DBG_DISCLOSETASK 0x8000 @@ -266,7 +271,7 @@ struct ibm_struct { int (*write) (char *); void (*exit) (void); void (*resume) (void); - void (*suspend) (pm_message_t state); + void (*suspend) (void); void (*shutdown) (void); struct list_head all_drivers; @@ -286,6 +291,7 @@ struct ibm_init_struct { char param[32]; int (*init) (struct ibm_init_struct *); + umode_t base_procfs_mode; struct ibm_struct *data; }; @@ -297,8 +303,8 @@ static struct { u32 hotkey_tablet:1; u32 light:1; u32 light_status:1; - u32 bright_16levels:1; u32 bright_acpimode:1; + u32 bright_unkfw:1; u32 wan:1; u32 uwb:1; u32 fan_ctrl_status_undef:1; @@ -358,18 +364,21 @@ struct tpacpi_led_classdev { struct led_classdev led_classdev; struct work_struct work; enum led_status_t new_state; - unsigned int led; + int led; }; +/* brightness level capabilities */ +static unsigned int bright_maxlvl; /* 0 = unknown */ + #ifdef CONFIG_THINKPAD_ACPI_DEBUGFACILITIES static int dbg_wlswemul; -static int tpacpi_wlsw_emulstate; +static bool tpacpi_wlsw_emulstate; static int dbg_bluetoothemul; -static int tpacpi_bluetooth_emulstate; +static bool tpacpi_bluetooth_emulstate; static int dbg_wwanemul; -static int tpacpi_wwan_emulstate; +static bool tpacpi_wwan_emulstate; static int dbg_uwbemul; -static int tpacpi_uwb_emulstate; +static bool tpacpi_uwb_emulstate; #endif @@ -377,34 +386,36 @@ static int tpacpi_uwb_emulstate; * Debugging helpers */ -#define dbg_printk(a_dbg_level, format, arg...) \ - do { if (dbg_level & (a_dbg_level)) \ - printk(TPACPI_DEBUG "%s: " format, __func__ , ## arg); \ - } while (0) +#define dbg_printk(a_dbg_level, format, arg...) \ +do { \ + if (dbg_level & (a_dbg_level)) \ + printk(KERN_DEBUG pr_fmt("%s: " format), \ + __func__, ##arg); \ +} while (0) #ifdef CONFIG_THINKPAD_ACPI_DEBUG #define vdbg_printk dbg_printk static const char *str_supported(int is_supported); #else -#define vdbg_printk(a_dbg_level, format, arg...) \ - do { } while (0) +static inline const char *str_supported(int is_supported) { return ""; } +#define vdbg_printk(a_dbg_level, format, arg...) \ + no_printk(format, ##arg) #endif static void tpacpi_log_usertask(const char * const what) { - printk(TPACPI_DEBUG "%s: access by process with PID %d\n", - what, task_tgid_vnr(current)); + printk(KERN_DEBUG pr_fmt("%s: access by process with PID %d\n"), + what, task_tgid_vnr(current)); } -#define tpacpi_disclose_usertask(what, format, arg...) \ - do { \ - if (unlikely( \ - (dbg_level & TPACPI_DBG_DISCLOSETASK) && \ - (tpacpi_lifecycle == TPACPI_LIFE_RUNNING))) { \ - printk(TPACPI_DEBUG "%s: PID %d: " format, \ - what, task_tgid_vnr(current), ## arg); \ - } \ - } while (0) +#define tpacpi_disclose_usertask(what, format, arg...) \ +do { \ + if (unlikely((dbg_level & TPACPI_DBG_DISCLOSETASK) && \ + (tpacpi_lifecycle == TPACPI_LIFE_RUNNING))) { \ + printk(KERN_DEBUG pr_fmt("%s: PID %d: " format), \ + what, task_tgid_vnr(current), ## arg); \ + } \ +} while (0) /* * Quirk handling helpers @@ -478,6 +489,15 @@ static unsigned long __init tpacpi_check_quirks( return 0; } +static inline bool __pure __init tpacpi_is_lenovo(void) +{ + return thinkpad_id.vendor == PCI_VENDOR_ID_LENOVO; +} + +static inline bool __pure __init tpacpi_is_ibm(void) +{ + return thinkpad_id.vendor == PCI_VENDOR_ID_IBM; +} /**************************************************************************** **************************************************************************** @@ -492,21 +512,13 @@ static unsigned long __init tpacpi_check_quirks( */ static acpi_handle root_handle; +static acpi_handle ec_handle; #define TPACPI_HANDLE(object, parent, paths...) \ static acpi_handle object##_handle; \ - static acpi_handle *object##_parent = &parent##_handle; \ - static char *object##_path; \ - static char *object##_paths[] = { paths } - -TPACPI_HANDLE(ec, root, "\\_SB.PCI0.ISA.EC0", /* 240, 240x */ - "\\_SB.PCI.ISA.EC", /* 570 */ - "\\_SB.PCI0.ISA0.EC0", /* 600e/x, 770e, 770x */ - "\\_SB.PCI0.ISA.EC", /* A21e, A2xm/p, T20-22, X20-21 */ - "\\_SB.PCI0.AD4S.EC0", /* i1400, R30 */ - "\\_SB.PCI0.ICH3.EC0", /* R31 */ - "\\_SB.PCI0.LPC.EC", /* all others */ - ); + static const acpi_handle * const object##_parent __initconst = \ + &parent##_handle; \ + static char *object##_paths[] __initdata = { paths } TPACPI_HANDLE(ecrd, ec, "ECRD"); /* 570 */ TPACPI_HANDLE(ecwr, ec, "ECWR"); /* 570 */ @@ -522,20 +534,12 @@ TPACPI_HANDLE(hkey, ec, "\\_SB.HKEY", /* 600e/x, 770e, 770x */ "HKEY", /* all others */ ); /* 570 */ -TPACPI_HANDLE(vid, root, "\\_SB.PCI.AGP.VGA", /* 570 */ - "\\_SB.PCI0.AGP0.VID0", /* 600e/x, 770x */ - "\\_SB.PCI0.VID0", /* 770e */ - "\\_SB.PCI0.VID", /* A21e, G4x, R50e, X30, X40 */ - "\\_SB.PCI0.AGP.VID", /* all others */ - ); /* R30, R31 */ - - /************************************************************************* * ACPI helpers */ static int acpi_evalf(acpi_handle handle, - void *res, char *method, char *fmt, ...) + int *res, char *method, char *fmt, ...) { char *fmt0 = fmt; struct acpi_object_list params; @@ -549,7 +553,7 @@ static int acpi_evalf(acpi_handle handle, int quiet; if (!*fmt) { - printk(TPACPI_ERR "acpi_evalf() called with empty format\n"); + pr_err("acpi_evalf() called with empty format\n"); return 0; } @@ -574,8 +578,9 @@ static int acpi_evalf(acpi_handle handle, break; /* add more types as needed */ default: - printk(TPACPI_ERR "acpi_evalf() called " + pr_err("acpi_evalf() called " "with invalid format character '%c'\n", c); + va_end(ap); return 0; } } @@ -592,23 +597,24 @@ static int acpi_evalf(acpi_handle handle, switch (res_type) { case 'd': /* int */ - if (res) - *(int *)res = out_obj.integer.value; - success = status == AE_OK && out_obj.type == ACPI_TYPE_INTEGER; + success = (status == AE_OK && + out_obj.type == ACPI_TYPE_INTEGER); + if (success && res) + *res = out_obj.integer.value; break; case 'v': /* void */ success = status == AE_OK; break; /* add more types as needed */ default: - printk(TPACPI_ERR "acpi_evalf() called " + pr_err("acpi_evalf() called " "with invalid format character '%c'\n", res_type); return 0; } if (!success && !quiet) - printk(TPACPI_ERR "acpi_evalf(%s, %s, ...) failed: %d\n", - method, fmt0, status); + pr_err("acpi_evalf(%s, %s, ...) failed: %s\n", + method, fmt0, acpi_format_exception(status)); return success; } @@ -659,11 +665,11 @@ static int issue_thinkpad_cmos_command(int cmos_cmd) #define TPACPI_ACPIHANDLE_INIT(object) \ drv_acpi_handle_init(#object, &object##_handle, *object##_parent, \ - object##_paths, ARRAY_SIZE(object##_paths), &object##_path) + object##_paths, ARRAY_SIZE(object##_paths)) -static void drv_acpi_handle_init(char *name, - acpi_handle *handle, acpi_handle parent, - char **paths, int num_paths, char **path) +static void __init drv_acpi_handle_init(const char *name, + acpi_handle *handle, const acpi_handle parent, + char **paths, const int num_paths) { int i; acpi_status status; @@ -674,10 +680,9 @@ static void drv_acpi_handle_init(char *name, for (i = 0; i < num_paths; i++) { status = acpi_get_handle(parent, paths[i], handle); if (ACPI_SUCCESS(status)) { - *path = paths[i]; dbg_printk(TPACPI_DBG_INIT, "Found ACPI handle %s for %s\n", - *path, name); + paths[i], name); return; } } @@ -687,6 +692,51 @@ static void drv_acpi_handle_init(char *name, *handle = NULL; } +static acpi_status __init tpacpi_acpi_handle_locate_callback(acpi_handle handle, + u32 level, void *context, void **return_value) +{ + struct acpi_device *dev; + if (!strcmp(context, "video")) { + if (acpi_bus_get_device(handle, &dev)) + return AE_OK; + if (strcmp(ACPI_VIDEO_HID, acpi_device_hid(dev))) + return AE_OK; + } + + *(acpi_handle *)return_value = handle; + + return AE_CTRL_TERMINATE; +} + +static void __init tpacpi_acpi_handle_locate(const char *name, + const char *hid, + acpi_handle *handle) +{ + acpi_status status; + acpi_handle device_found; + + BUG_ON(!name || !handle); + vdbg_printk(TPACPI_DBG_INIT, + "trying to locate ACPI handle for %s, using HID %s\n", + name, hid ? hid : "NULL"); + + memset(&device_found, 0, sizeof(device_found)); + status = acpi_get_devices(hid, tpacpi_acpi_handle_locate_callback, + (void *)name, &device_found); + + *handle = NULL; + + if (ACPI_SUCCESS(status)) { + *handle = device_found; + dbg_printk(TPACPI_DBG_INIT, + "Found ACPI handle for %s\n", name); + } else { + vdbg_printk(TPACPI_DBG_INIT, + "Could not locate an ACPI handle for %s: %s\n", + name, acpi_format_exception(status)); + } +} + static void dispatch_acpi_notify(acpi_handle handle, u32 event, void *data) { struct ibm_struct *ibm = data; @@ -715,8 +765,7 @@ static int __init setup_acpi_notify(struct ibm_struct *ibm) rc = acpi_bus_get_device(*ibm->acpi->handle, &ibm->acpi->device); if (rc < 0) { - printk(TPACPI_ERR "acpi_bus_get_device(%s) failed: %d\n", - ibm->name, rc); + pr_err("acpi_bus_get_device(%s) failed: %d\n", ibm->name, rc); return -ENODEV; } @@ -729,13 +778,11 @@ 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) { - printk(TPACPI_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 { - printk(TPACPI_ERR - "acpi_install_notify_handler(%s) failed: %d\n", - ibm->name, status); + pr_err("acpi_install_notify_handler(%s) failed: %s\n", + ibm->name, acpi_format_exception(status)); } return -ENODEV; } @@ -759,8 +806,7 @@ static int __init register_tpacpi_subdriver(struct ibm_struct *ibm) ibm->acpi->driver = kzalloc(sizeof(struct acpi_driver), GFP_KERNEL); if (!ibm->acpi->driver) { - printk(TPACPI_ERR - "failed to allocate memory for ibm->acpi->driver\n"); + pr_err("failed to allocate memory for ibm->acpi->driver\n"); return -ENOMEM; } @@ -771,7 +817,7 @@ static int __init register_tpacpi_subdriver(struct ibm_struct *ibm) rc = acpi_bus_register_driver(ibm->acpi->driver); if (rc < 0) { - printk(TPACPI_ERR "acpi_bus_register_driver(%s) failed: %d\n", + pr_err("acpi_bus_register_driver(%s) failed: %d\n", ibm->name, rc); kfree(ibm->acpi->driver); ibm->acpi->driver = NULL; @@ -801,14 +847,14 @@ static int dispatch_proc_show(struct seq_file *m, void *v) static int dispatch_proc_open(struct inode *inode, struct file *file) { - return single_open(file, dispatch_proc_show, PDE(inode)->data); + return single_open(file, dispatch_proc_show, PDE_DATA(inode)); } static ssize_t dispatch_proc_write(struct file *file, const char __user *userbuf, size_t count, loff_t *pos) { - struct ibm_struct *ibm = PDE(file->f_path.dentry->d_inode)->data; + struct ibm_struct *ibm = PDE_DATA(file_inode(file)); char *kernbuf; int ret; @@ -878,8 +924,8 @@ static struct input_dev *tpacpi_inputdev; static struct mutex tpacpi_inputdev_send_mutex; static LIST_HEAD(tpacpi_all_drivers); -static int tpacpi_suspend_handler(struct platform_device *pdev, - pm_message_t state) +#ifdef CONFIG_PM_SLEEP +static int tpacpi_suspend_handler(struct device *dev) { struct ibm_struct *ibm, *itmp; @@ -887,13 +933,13 @@ static int tpacpi_suspend_handler(struct platform_device *pdev, &tpacpi_all_drivers, all_drivers) { if (ibm->suspend) - (ibm->suspend)(state); + (ibm->suspend)(); } return 0; } -static int tpacpi_resume_handler(struct platform_device *pdev) +static int tpacpi_resume_handler(struct device *dev) { struct ibm_struct *ibm, *itmp; @@ -906,6 +952,10 @@ static int tpacpi_resume_handler(struct platform_device *pdev) return 0; } +#endif + +static SIMPLE_DEV_PM_OPS(tpacpi_pm, + tpacpi_suspend_handler, tpacpi_resume_handler); static void tpacpi_shutdown_handler(struct platform_device *pdev) { @@ -923,9 +973,8 @@ static struct platform_driver tpacpi_pdriver = { .driver = { .name = TPACPI_DRVR_NAME, .owner = THIS_MODULE, + .pm = &tpacpi_pm, }, - .suspend = tpacpi_suspend_handler, - .resume = tpacpi_resume_handler, .shutdown = tpacpi_shutdown_handler, }; @@ -1029,89 +1078,14 @@ static int parse_strtoul(const char *buf, static void tpacpi_disable_brightness_delay(void) { if (acpi_evalf(hkey_handle, NULL, "PWMS", "qvd", 0)) - printk(TPACPI_NOTICE - "ACPI backlight control delay disabled\n"); -} - -static int __init tpacpi_query_bcl_levels(acpi_handle handle) -{ - struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; - union acpi_object *obj; - int rc; - - if (ACPI_SUCCESS(acpi_evaluate_object(handle, NULL, NULL, &buffer))) { - obj = (union acpi_object *)buffer.pointer; - if (!obj || (obj->type != ACPI_TYPE_PACKAGE)) { - printk(TPACPI_ERR "Unknown _BCL data, " - "please report this to %s\n", TPACPI_MAIL); - rc = 0; - } else { - rc = obj->package.count; - } - } else { - return 0; - } - - kfree(buffer.pointer); - return rc; -} - -static acpi_status __init tpacpi_acpi_walk_find_bcl(acpi_handle handle, - u32 lvl, void *context, void **rv) -{ - char name[ACPI_PATH_SEGMENT_LENGTH]; - struct acpi_buffer buffer = { sizeof(name), &name }; - - if (ACPI_SUCCESS(acpi_get_name(handle, ACPI_SINGLE_NAME, &buffer)) && - !strncmp("_BCL", name, sizeof(name) - 1)) { - BUG_ON(!rv || !*rv); - **(int **)rv = tpacpi_query_bcl_levels(handle); - return AE_CTRL_TERMINATE; - } else { - return AE_OK; - } -} - -/* - * Returns 0 (no ACPI _BCL or _BCL invalid), or size of brightness map - */ -static int __init tpacpi_check_std_acpi_brightness_support(void) -{ - int status; - int bcl_levels = 0; - void *bcl_ptr = &bcl_levels; - - if (!vid_handle) { - TPACPI_ACPIHANDLE_INIT(vid); - } - if (!vid_handle) - return 0; - - /* - * Search for a _BCL method, and execute it. This is safe on all - * ThinkPads, and as a side-effect, _BCL will place a Lenovo Vista - * BIOS in ACPI backlight control mode. We do NOT have to care - * about calling the _BCL method in an enabled video device, any - * will do for our purposes. - */ - - status = acpi_walk_namespace(ACPI_TYPE_METHOD, vid_handle, 3, - tpacpi_acpi_walk_find_bcl, NULL, NULL, - &bcl_ptr); - - if (ACPI_SUCCESS(status) && bcl_levels > 2) { - tp_features.bright_acpimode = 1; - return (bcl_levels - 2); - } - - return 0; + pr_notice("ACPI backlight control delay disabled\n"); } static void printk_deprecated_attribute(const char * const what, const char * const details) { tpacpi_log_usertask("deprecated sysfs attribute"); - printk(TPACPI_WARN "WARNING: sysfs attribute %s is deprecated and " + pr_warn("WARNING: sysfs attribute %s is deprecated and " "will be removed. %s\n", what, details); } @@ -1286,8 +1260,7 @@ static int __init tpacpi_new_rfkill(const enum tpacpi_rfk_id id, &tpacpi_rfk_rfkill_ops, atp_rfk); if (!atp_rfk || !atp_rfk->rfkill) { - printk(TPACPI_ERR - "failed to allocate memory for rfkill class\n"); + pr_err("failed to allocate memory for rfkill class\n"); kfree(atp_rfk); return -ENOMEM; } @@ -1297,9 +1270,8 @@ static int __init tpacpi_new_rfkill(const enum tpacpi_rfk_id id, sw_status = (tp_rfkops->get_status)(); if (sw_status < 0) { - printk(TPACPI_ERR - "failed to read initial state for %s, error %d\n", - name, sw_status); + pr_err("failed to read initial state for %s, error %d\n", + name, sw_status); } else { sw_state = (sw_status == TPACPI_RFK_RADIO_OFF); if (set_default) { @@ -1313,9 +1285,7 @@ static int __init tpacpi_new_rfkill(const enum tpacpi_rfk_id id, res = rfkill_register(atp_rfk->rfkill); if (res < 0) { - printk(TPACPI_ERR - "failed to register %s rfkill switch: %d\n", - name, res); + pr_err("failed to register %s rfkill switch: %d\n", name, res); rfkill_destroy(atp_rfk->rfkill); kfree(atp_rfk); return res; @@ -1323,7 +1293,7 @@ static int __init tpacpi_new_rfkill(const enum tpacpi_rfk_id id, tpacpi_rfkill_switches[id] = atp_rfk; - printk(TPACPI_INFO "rfkill switch %s: radio is %sblocked\n", + pr_info("rfkill switch %s: radio is %sblocked\n", name, (sw_state || hw_state) ? "" : "un"); return 0; } @@ -1667,7 +1637,7 @@ static void tpacpi_remove_driver_attributes(struct device_driver *drv) * Table of recommended minimum BIOS versions * * Reasons for listing: - * 1. Stable BIOS, listed because the unknown ammount of + * 1. Stable BIOS, listed because the unknown amount of * bugs and bad ACPI behaviour on older versions * * 2. BIOS or EC fw with known bugs that trigger on Linux @@ -1847,10 +1817,8 @@ static void __init tpacpi_check_outdated_fw(void) * broken, or really stable to begin with, so it is * best if the user upgrades the firmware anyway. */ - printk(TPACPI_WARN - "WARNING: Outdated ThinkPad BIOS/EC firmware\n"); - printk(TPACPI_WARN - "WARNING: This firmware may be missing critical bug " + 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"); } } @@ -1870,34 +1838,9 @@ static bool __init tpacpi_is_fw_known(void) ****************************************************************************/ /************************************************************************* - * thinkpad-acpi init subdriver + * thinkpad-acpi metadata subdriver */ -static int __init thinkpad_acpi_driver_init(struct ibm_init_struct *iibm) -{ - printk(TPACPI_INFO "%s v%s\n", TPACPI_DESC, TPACPI_VERSION); - printk(TPACPI_INFO "%s\n", TPACPI_URL); - - printk(TPACPI_INFO "ThinkPad BIOS %s, EC %s\n", - (thinkpad_id.bios_version_str) ? - thinkpad_id.bios_version_str : "unknown", - (thinkpad_id.ec_version_str) ? - thinkpad_id.ec_version_str : "unknown"); - - if (thinkpad_id.vendor && thinkpad_id.model_str) - printk(TPACPI_INFO "%s %s, model %s\n", - (thinkpad_id.vendor == PCI_VENDOR_ID_IBM) ? - "IBM" : ((thinkpad_id.vendor == - PCI_VENDOR_ID_LENOVO) ? - "Lenovo" : "Unknown vendor"), - thinkpad_id.model_str, - (thinkpad_id.nummodel_str) ? - thinkpad_id.nummodel_str : "unknown"); - - tpacpi_check_outdated_fw(); - return 0; -} - static int thinkpad_acpi_driver_read(struct seq_file *m) { seq_printf(m, "driver:\t\t%s\n", TPACPI_DESC); @@ -1960,6 +1903,17 @@ enum { /* hot key scan codes (derived from ACPI DSDT) */ TP_ACPI_HOTKEYSCAN_VOLUMEDOWN, TP_ACPI_HOTKEYSCAN_MUTE, TP_ACPI_HOTKEYSCAN_THINKPAD, + TP_ACPI_HOTKEYSCAN_UNK1, + TP_ACPI_HOTKEYSCAN_UNK2, + TP_ACPI_HOTKEYSCAN_UNK3, + TP_ACPI_HOTKEYSCAN_UNK4, + TP_ACPI_HOTKEYSCAN_UNK5, + TP_ACPI_HOTKEYSCAN_UNK6, + TP_ACPI_HOTKEYSCAN_UNK7, + TP_ACPI_HOTKEYSCAN_UNK8, + + /* Hotkey keymap size */ + TPACPI_HOTKEY_MAP_LEN }; enum { /* Keys/events available through NVRAM polling */ @@ -2013,9 +1967,6 @@ struct tp_nvram_state { /* kthread for the hotkey poller */ static struct task_struct *tpacpi_hotkey_task; -/* Acquired while the poller kthread is running, use to sync start/stop */ -static struct mutex hotkey_thread_mutex; - /* * Acquire mutex to write poller control variables as an * atomic block. @@ -2074,14 +2025,13 @@ static u32 hotkey_driver_mask; /* events needed by the driver */ static u32 hotkey_user_mask; /* events visible to userspace */ static u32 hotkey_acpi_mask; /* events enabled in firmware */ -static unsigned int hotkey_report_mode; - static u16 *hotkey_keycode_map; static struct attribute_set *hotkey_dev_attributes; static void tpacpi_driver_event(const unsigned int hkey_event); static void hotkey_driver_event(const unsigned int scancode); +static void hotkey_poll_setup(const bool may_warn); /* HKEY.MHKG() return bits */ #define TP_HOTKEY_TABLET_MASK (1 << 3) @@ -2152,9 +2102,7 @@ void static hotkey_mask_warn_incomplete_mask(void) (hotkey_all_mask | TPACPI_HKEY_NVRAM_KNOWN_MASK); if (wantedmask) - printk(TPACPI_NOTICE - "required events 0x%08x not enabled!\n", - wantedmask); + pr_notice("required events 0x%08x not enabled!\n", wantedmask); } /* @@ -2192,10 +2140,9 @@ static int hotkey_mask_set(u32 mask) * a given event. */ if (!hotkey_mask_get() && !rc && (fwmask & ~hotkey_acpi_mask)) { - printk(TPACPI_NOTICE - "asked for hotkey mask 0x%08x, but " - "firmware forced it to 0x%08x\n", - fwmask, hotkey_acpi_mask); + pr_notice("asked for hotkey mask 0x%08x, but " + "firmware forced it to 0x%08x\n", + fwmask, hotkey_acpi_mask); } if (tpacpi_lifecycle != TPACPI_LIFE_EXITING) @@ -2219,13 +2166,11 @@ static int hotkey_user_mask_set(const u32 mask) (mask == 0xffff || mask == 0xffffff || mask == 0xffffffff)) { tp_warned.hotkey_mask_ff = 1; - printk(TPACPI_NOTICE - "setting the hotkey mask to 0x%08x is likely " - "not the best way to go about it\n", mask); - printk(TPACPI_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. @@ -2264,6 +2209,8 @@ static int tpacpi_hotkey_driver_mask_set(const u32 mask) rc = hotkey_mask_set((hotkey_acpi_mask | hotkey_driver_mask) & ~hotkey_source_mask); + hotkey_poll_setup(true); + mutex_unlock(&hotkey_mutex); return rc; @@ -2309,16 +2256,12 @@ static void tpacpi_input_send_key(const unsigned int scancode) if (keycode != KEY_RESERVED) { mutex_lock(&tpacpi_inputdev_send_mutex); + input_event(tpacpi_inputdev, EV_MSC, MSC_SCAN, scancode); input_report_key(tpacpi_inputdev, keycode, 1); - if (keycode == KEY_UNKNOWN) - input_event(tpacpi_inputdev, EV_MSC, MSC_SCAN, - scancode); input_sync(tpacpi_inputdev); + input_event(tpacpi_inputdev, EV_MSC, MSC_SCAN, scancode); input_report_key(tpacpi_inputdev, keycode, 0); - if (keycode == KEY_UNKNOWN) - input_event(tpacpi_inputdev, EV_MSC, MSC_SCAN, - scancode); input_sync(tpacpi_inputdev); mutex_unlock(&tpacpi_inputdev_send_mutex); @@ -2340,10 +2283,6 @@ static struct tp_acpi_drv_struct ibm_hotkey_acpidriver; static void tpacpi_hotkey_send_key(unsigned int scancode) { tpacpi_input_send_key_masked(scancode); - if (hotkey_report_mode < 2) { - acpi_bus_generate_proc_event(ibm_hotkey_acpidriver.device, - 0x80, TP_HKEY_EV_HOTKEY_BASE + scancode); - } } static void hotkey_read_nvram(struct tp_nvram_state *n, const u32 m) @@ -2382,24 +2321,56 @@ static void hotkey_read_nvram(struct tp_nvram_state *n, const u32 m) } } +#define TPACPI_COMPARE_KEY(__scancode, __member) \ +do { \ + if ((event_mask & (1 << __scancode)) && \ + oldn->__member != newn->__member) \ + tpacpi_hotkey_send_key(__scancode); \ +} while (0) + +#define TPACPI_MAY_SEND_KEY(__scancode) \ +do { \ + if (event_mask & (1 << __scancode)) \ + tpacpi_hotkey_send_key(__scancode); \ +} while (0) + +static void issue_volchange(const unsigned int oldvol, + const unsigned int newvol, + const u32 event_mask) +{ + unsigned int i = oldvol; + + while (i > newvol) { + TPACPI_MAY_SEND_KEY(TP_ACPI_HOTKEYSCAN_VOLUMEDOWN); + i--; + } + while (i < newvol) { + TPACPI_MAY_SEND_KEY(TP_ACPI_HOTKEYSCAN_VOLUMEUP); + i++; + } +} + +static void issue_brightnesschange(const unsigned int oldbrt, + const unsigned int newbrt, + const u32 event_mask) +{ + unsigned int i = oldbrt; + + while (i > newbrt) { + TPACPI_MAY_SEND_KEY(TP_ACPI_HOTKEYSCAN_FNEND); + i--; + } + while (i < newbrt) { + TPACPI_MAY_SEND_KEY(TP_ACPI_HOTKEYSCAN_FNHOME); + i++; + } +} + static void hotkey_compare_and_issue_event(struct tp_nvram_state *oldn, struct tp_nvram_state *newn, const u32 event_mask) { -#define TPACPI_COMPARE_KEY(__scancode, __member) \ - do { \ - if ((event_mask & (1 << __scancode)) && \ - oldn->__member != newn->__member) \ - tpacpi_hotkey_send_key(__scancode); \ - } while (0) - -#define TPACPI_MAY_SEND_KEY(__scancode) \ - do { \ - if (event_mask & (1 << __scancode)) \ - tpacpi_hotkey_send_key(__scancode); \ - } while (0) - TPACPI_COMPARE_KEY(TP_ACPI_HOTKEYSCAN_THINKPAD, thinkpad_toggle); TPACPI_COMPARE_KEY(TP_ACPI_HOTKEYSCAN_FNSPACE, zoom_toggle); TPACPI_COMPARE_KEY(TP_ACPI_HOTKEYSCAN_FNF7, display_toggle); @@ -2409,41 +2380,63 @@ static void hotkey_compare_and_issue_event(struct tp_nvram_state *oldn, TPACPI_COMPARE_KEY(TP_ACPI_HOTKEYSCAN_FNF8, displayexp_toggle); - /* handle volume */ - if (oldn->volume_toggle != newn->volume_toggle) { - if (oldn->mute != newn->mute) { + /* + * Handle volume + * + * This code is supposed to duplicate the IBM firmware behaviour: + * - Pressing MUTE issues mute hotkey message, even when already mute + * - Pressing Volume up/down issues volume up/down hotkey messages, + * even when already at maximum or minimum volume + * - The act of unmuting issues volume up/down notification, + * depending which key was used to unmute + * + * We are constrained to what the NVRAM can tell us, which is not much + * and certainly not enough if more than one volume hotkey was pressed + * since the last poll cycle. + * + * Just to make our life interesting, some newer Lenovo ThinkPads have + * bugs in the BIOS and may fail to update volume_toggle properly. + */ + if (newn->mute) { + /* muted */ + if (!oldn->mute || + oldn->volume_toggle != newn->volume_toggle || + oldn->volume_level != newn->volume_level) { + /* recently muted, or repeated mute keypress, or + * multiple presses ending in mute */ + issue_volchange(oldn->volume_level, newn->volume_level, + event_mask); TPACPI_MAY_SEND_KEY(TP_ACPI_HOTKEYSCAN_MUTE); } - if (oldn->volume_level > newn->volume_level) { - TPACPI_MAY_SEND_KEY(TP_ACPI_HOTKEYSCAN_VOLUMEDOWN); - } else if (oldn->volume_level < newn->volume_level) { + } else { + /* unmute */ + if (oldn->mute) { + /* recently unmuted, issue 'unmute' keypress */ TPACPI_MAY_SEND_KEY(TP_ACPI_HOTKEYSCAN_VOLUMEUP); - } else if (oldn->mute == newn->mute) { - /* repeated key presses that didn't change state */ - if (newn->mute) { - TPACPI_MAY_SEND_KEY(TP_ACPI_HOTKEYSCAN_MUTE); - } else if (newn->volume_level != 0) { - TPACPI_MAY_SEND_KEY(TP_ACPI_HOTKEYSCAN_VOLUMEUP); - } else { + } + if (oldn->volume_level != newn->volume_level) { + issue_volchange(oldn->volume_level, newn->volume_level, + event_mask); + } else if (oldn->volume_toggle != newn->volume_toggle) { + /* repeated vol up/down keypress at end of scale ? */ + if (newn->volume_level == 0) TPACPI_MAY_SEND_KEY(TP_ACPI_HOTKEYSCAN_VOLUMEDOWN); - } + else if (newn->volume_level >= TP_NVRAM_LEVEL_VOLUME_MAX) + TPACPI_MAY_SEND_KEY(TP_ACPI_HOTKEYSCAN_VOLUMEUP); } } /* handle brightness */ - if (oldn->brightness_toggle != newn->brightness_toggle) { - if (oldn->brightness_level < newn->brightness_level) { - TPACPI_MAY_SEND_KEY(TP_ACPI_HOTKEYSCAN_FNHOME); - } else if (oldn->brightness_level > newn->brightness_level) { + if (oldn->brightness_level != newn->brightness_level) { + issue_brightnesschange(oldn->brightness_level, + newn->brightness_level, event_mask); + } else if (oldn->brightness_toggle != newn->brightness_toggle) { + /* repeated key presses that didn't change state */ + if (newn->brightness_level == 0) TPACPI_MAY_SEND_KEY(TP_ACPI_HOTKEYSCAN_FNEND); - } else { - /* repeated key presses that didn't change state */ - if (newn->brightness_level != 0) { - TPACPI_MAY_SEND_KEY(TP_ACPI_HOTKEYSCAN_FNHOME); - } else { - TPACPI_MAY_SEND_KEY(TP_ACPI_HOTKEYSCAN_FNEND); - } - } + else if (newn->brightness_level >= bright_maxlvl + && !tp_features.bright_unkfw) + TPACPI_MAY_SEND_KEY(TP_ACPI_HOTKEYSCAN_FNHOME); } #undef TPACPI_COMPARE_KEY @@ -2463,10 +2456,9 @@ static int hotkey_kthread(void *data) u32 poll_mask, event_mask; unsigned int si, so; unsigned long t; - unsigned int change_detector, must_reset; + unsigned int change_detector; unsigned int poll_freq; - - mutex_lock(&hotkey_thread_mutex); + bool was_frozen; if (tpacpi_lifecycle == TPACPI_LIFE_EXITING) goto exit; @@ -2495,14 +2487,14 @@ static int hotkey_kthread(void *data) t = 100; /* should never happen... */ } t = msleep_interruptible(t); - if (unlikely(kthread_should_stop())) + if (unlikely(kthread_freezable_should_stop(&was_frozen))) break; - must_reset = try_to_freeze(); - if (t > 0 && !must_reset) + + if (t > 0 && !was_frozen) continue; mutex_lock(&hotkey_thread_data_mutex); - if (must_reset || hotkey_config_change != change_detector) { + if (was_frozen || hotkey_config_change != change_detector) { /* forget old state on thaw or config change */ si = so; t = 0; @@ -2527,7 +2519,6 @@ static int hotkey_kthread(void *data) } exit: - mutex_unlock(&hotkey_thread_mutex); return 0; } @@ -2535,20 +2526,13 @@ exit: static void hotkey_poll_stop_sync(void) { if (tpacpi_hotkey_task) { - if (frozen(tpacpi_hotkey_task) || - freezing(tpacpi_hotkey_task)) - thaw_process(tpacpi_hotkey_task); - kthread_stop(tpacpi_hotkey_task); tpacpi_hotkey_task = NULL; - mutex_lock(&hotkey_thread_mutex); - /* at this point, the thread did exit */ - mutex_unlock(&hotkey_thread_mutex); } } /* call with hotkey_mutex held */ -static void hotkey_poll_setup(bool may_warn) +static void hotkey_poll_setup(const bool may_warn) { const u32 poll_driver_mask = hotkey_driver_mask & hotkey_source_mask; const u32 poll_user_mask = hotkey_user_mask & hotkey_source_mask; @@ -2561,8 +2545,7 @@ static void hotkey_poll_setup(bool may_warn) NULL, TPACPI_NVRAM_KTHREAD_NAME); if (IS_ERR(tpacpi_hotkey_task)) { tpacpi_hotkey_task = NULL; - printk(TPACPI_ERR - "could not create kernel thread " + pr_err("could not create kernel thread " "for hotkey polling\n"); } } @@ -2570,16 +2553,15 @@ static void hotkey_poll_setup(bool may_warn) hotkey_poll_stop_sync(); if (may_warn && (poll_driver_mask || poll_user_mask) && hotkey_poll_freq == 0) { - printk(TPACPI_NOTICE - "hot keys 0x%08x and/or events 0x%08x " - "require polling, which is currently " - "disabled\n", - poll_user_mask, poll_driver_mask); + pr_notice("hot keys 0x%08x and/or events 0x%08x " + "require polling, which is currently " + "disabled\n", + poll_user_mask, poll_driver_mask); } } } -static void hotkey_poll_setup_safe(bool may_warn) +static void hotkey_poll_setup_safe(const bool may_warn) { mutex_lock(&hotkey_mutex); hotkey_poll_setup(may_warn); @@ -2597,7 +2579,11 @@ static void hotkey_poll_set_freq(unsigned int freq) #else /* CONFIG_THINKPAD_ACPI_HOTKEY_POLL */ -static void hotkey_poll_setup_safe(bool __unused) +static void hotkey_poll_setup(const bool __unused) +{ +} + +static void hotkey_poll_setup_safe(const bool __unused) { } @@ -2607,16 +2593,11 @@ static int hotkey_inputdev_open(struct input_dev *dev) { switch (tpacpi_lifecycle) { case TPACPI_LIFE_INIT: - /* - * hotkey_init will call hotkey_poll_setup_safe - * at the appropriate moment - */ - return 0; - case TPACPI_LIFE_EXITING: - return -EBUSY; case TPACPI_LIFE_RUNNING: hotkey_poll_setup_safe(false); return 0; + case TPACPI_LIFE_EXITING: + return -EBUSY; } /* Should only happen if tpacpi_lifecycle is corrupt */ @@ -2627,7 +2608,7 @@ static int hotkey_inputdev_open(struct input_dev *dev) static void hotkey_inputdev_close(struct input_dev *dev) { /* disable hotkey polling when possible */ - if (tpacpi_lifecycle == TPACPI_LIFE_RUNNING && + if (tpacpi_lifecycle != TPACPI_LIFE_EXITING && !(hotkey_source_mask & hotkey_driver_mask)) hotkey_poll_setup_safe(false); } @@ -2799,13 +2780,13 @@ static ssize_t hotkey_source_mask_store(struct device *dev, mutex_unlock(&hotkey_mutex); if (rc < 0) - printk(TPACPI_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) - printk(TPACPI_NOTICE "hotkey_source_mask: " - "some important events were disabled: " - "0x%04x\n", r_ev); + 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); @@ -2902,18 +2883,6 @@ static void hotkey_tablet_mode_notify_change(void) "hotkey_tablet_mode"); } -/* sysfs hotkey report_mode -------------------------------------------- */ -static ssize_t hotkey_report_mode_show(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - return snprintf(buf, PAGE_SIZE, "%d\n", - (hotkey_report_mode != 0) ? hotkey_report_mode : 1); -} - -static struct device_attribute dev_attr_hotkey_report_mode = - __ATTR(hotkey_report_mode, S_IRUGO, hotkey_report_mode_show, NULL); - /* sysfs wakeup reason (pollable) -------------------------------------- */ static ssize_t hotkey_wakeup_reason_show(struct device *dev, struct device_attribute *attr, @@ -2955,7 +2924,6 @@ static struct attribute *hotkey_attributes[] __initdata = { &dev_attr_hotkey_enable.attr, &dev_attr_hotkey_bios_enabled.attr, &dev_attr_hotkey_bios_mask.attr, - &dev_attr_hotkey_report_mode.attr, &dev_attr_hotkey_wakeup_reason.attr, &dev_attr_hotkey_wakeup_hotunplug_complete.attr, &dev_attr_hotkey_mask.attr, @@ -2979,7 +2947,7 @@ static void tpacpi_send_radiosw_update(void) * rfkill input events, or we will race the rfkill core input * handler. * - * tpacpi_inputdev_send_mutex works as a syncronization point + * tpacpi_inputdev_send_mutex works as a synchronization point * for the above. * * We optimize to avoid numerous calls to hotkey_get_wlsw. @@ -3027,8 +2995,6 @@ static void hotkey_exit(void) if (hotkey_dev_attributes) delete_attr_set(hotkey_dev_attributes, &tpacpi_pdev->dev.kobj); - kfree(hotkey_keycode_map); - dbg_printk(TPACPI_DBG_EXIT | TPACPI_DBG_HKEY, "restoring original HKEY status and mask\n"); /* yes, there is a bitwise or below, we want the @@ -3036,8 +3002,7 @@ static void hotkey_exit(void) if (((tp_features.hotkey_mask && hotkey_mask_set(hotkey_orig_mask)) | hotkey_status_set(false)) != 0) - printk(TPACPI_ERR - "failed to restore hot key mask " + pr_err("failed to restore hot key mask " "to BIOS defaults\n"); } @@ -3079,6 +3044,9 @@ static const struct tpacpi_quirk tpacpi_hotkey_qtable[] __initconst = { TPACPI_Q_IBM('1', 'D', TPACPI_HK_Q_INIMASK), /* X22, X23, X24 */ }; +typedef u16 tpacpi_keymap_entry_t; +typedef tpacpi_keymap_entry_t tpacpi_keymap_t[TPACPI_HOTKEY_MAP_LEN]; + static int __init hotkey_init(struct ibm_init_struct *iibm) { /* Requirements for changing the default keymaps: @@ -3110,9 +3078,17 @@ static int __init hotkey_init(struct ibm_init_struct *iibm) * If the above is too much to ask, don't change the keymap. * Ask the thinkpad-acpi maintainer to do it, instead. */ - static u16 ibm_keycode_map[] __initdata = { + + enum keymap_index { + TPACPI_KEYMAP_IBM_GENERIC = 0, + TPACPI_KEYMAP_LENOVO_GENERIC, + }; + + static const tpacpi_keymap_t tpacpi_keymaps[] __initconst = { + /* Generic keymap for IBM ThinkPads */ + [TPACPI_KEYMAP_IBM_GENERIC] = { /* Scan Codes 0x00 to 0x0B: ACPI HKEY FN+F1..F12 */ - KEY_FN_F1, KEY_FN_F2, KEY_COFFEE, KEY_SLEEP, + KEY_FN_F1, KEY_BATTERY, KEY_COFFEE, KEY_SLEEP, KEY_WLAN, KEY_FN_F6, KEY_SWITCHVIDEOMODE, KEY_FN_F8, KEY_FN_F9, KEY_FN_F10, KEY_FN_F11, KEY_SUSPEND, @@ -3143,11 +3119,13 @@ static int __init hotkey_init(struct ibm_init_struct *iibm) /* (assignments unknown, please report if found) */ KEY_UNKNOWN, KEY_UNKNOWN, KEY_UNKNOWN, KEY_UNKNOWN, KEY_UNKNOWN, KEY_UNKNOWN, KEY_UNKNOWN, KEY_UNKNOWN, - }; - static u16 lenovo_keycode_map[] __initdata = { + }, + + /* Generic keymap for Lenovo ThinkPads */ + [TPACPI_KEYMAP_LENOVO_GENERIC] = { /* Scan Codes 0x00 to 0x0B: ACPI HKEY FN+F1..F12 */ KEY_FN_F1, KEY_COFFEE, KEY_BATTERY, KEY_SLEEP, - KEY_WLAN, KEY_FN_F6, KEY_SWITCHVIDEOMODE, KEY_FN_F8, + KEY_WLAN, KEY_CAMERA, KEY_SWITCHVIDEOMODE, KEY_FN_F8, KEY_FN_F9, KEY_FN_F10, KEY_FN_F11, KEY_SUSPEND, /* Scan codes 0x0C to 0x1F: Other ACPI HKEY hot keys */ @@ -3184,13 +3162,38 @@ static int __init hotkey_init(struct ibm_init_struct *iibm) KEY_VENDOR, /* 0x17: Thinkpad/AccessIBM/Lenovo */ /* (assignments unknown, please report if found) */ - KEY_UNKNOWN, KEY_UNKNOWN, KEY_UNKNOWN, KEY_UNKNOWN, - KEY_UNKNOWN, KEY_UNKNOWN, KEY_UNKNOWN, KEY_UNKNOWN, + KEY_UNKNOWN, KEY_UNKNOWN, + + /* + * The mic mute button only sends 0x1a. It does not + * automatically mute the mic or change the mute light. + */ + KEY_MICMUTE, /* 0x1a: Mic mute (since ?400 or so) */ + + /* (assignments unknown, please report if found) */ + KEY_UNKNOWN, + + /* Extra keys in use since the X240 / T440 / T540 */ + KEY_CONFIG, KEY_SEARCH, KEY_SCALE, KEY_COMPUTER, + }, }; -#define TPACPI_HOTKEY_MAP_LEN ARRAY_SIZE(ibm_keycode_map) -#define TPACPI_HOTKEY_MAP_SIZE sizeof(ibm_keycode_map) -#define TPACPI_HOTKEY_MAP_TYPESIZE sizeof(ibm_keycode_map[0]) + static const struct tpacpi_quirk tpacpi_keymap_qtable[] __initconst = { + /* Generic maps (fallback) */ + { + .vendor = PCI_VENDOR_ID_IBM, + .bios = TPACPI_MATCH_ANY, .ec = TPACPI_MATCH_ANY, + .quirks = TPACPI_KEYMAP_IBM_GENERIC, + }, + { + .vendor = PCI_VENDOR_ID_LENOVO, + .bios = TPACPI_MATCH_ANY, .ec = TPACPI_MATCH_ANY, + .quirks = TPACPI_KEYMAP_LENOVO_GENERIC, + }, + }; + +#define TPACPI_HOTKEY_MAP_SIZE sizeof(tpacpi_keymap_t) +#define TPACPI_HOTKEY_MAP_TYPESIZE sizeof(tpacpi_keymap_entry_t) int res, i; int status; @@ -3199,6 +3202,7 @@ static int __init hotkey_init(struct ibm_init_struct *iibm) bool tabletsw_state = false; unsigned long quirks; + unsigned long keymap_id; vdbg_printk(TPACPI_DBG_INIT | TPACPI_DBG_HKEY, "initializing hotkey subdriver\n"); @@ -3211,7 +3215,6 @@ static int __init hotkey_init(struct ibm_init_struct *iibm) mutex_init(&hotkey_mutex); #ifdef CONFIG_THINKPAD_ACPI_HOTKEY_POLL - mutex_init(&hotkey_thread_mutex); mutex_init(&hotkey_thread_data_mutex); #endif @@ -3248,10 +3251,9 @@ static int __init hotkey_init(struct ibm_init_struct *iibm) for HKEY interface version 0x100 */ if (acpi_evalf(hkey_handle, &hkeyv, "MHKV", "qd")) { if ((hkeyv >> 8) != 1) { - printk(TPACPI_ERR "unknown version of the " - "HKEY interface: 0x%x\n", hkeyv); - printk(TPACPI_ERR "please report this to %s\n", - TPACPI_MAIL); + pr_err("unknown version of the HKEY interface: 0x%x\n", + hkeyv); + pr_err("please report this to %s\n", TPACPI_MAIL); } else { /* * MHKV 0x100 in A31, R40, R40e, @@ -3264,8 +3266,7 @@ static int __init hotkey_init(struct ibm_init_struct *iibm) /* Paranoia check AND init hotkey_all_mask */ if (!acpi_evalf(hkey_handle, &hotkey_all_mask, "MHKA", "qd")) { - printk(TPACPI_ERR - "missing MHKA handler, " + pr_err("missing MHKA handler, " "please report this to %s\n", TPACPI_MAIL); /* Fallback: pre-init for FN+F3,F4,F12 */ @@ -3303,16 +3304,14 @@ static int __init hotkey_init(struct ibm_init_struct *iibm) if (dbg_wlswemul) { tp_features.hotkey_wlsw = 1; radiosw_state = !!tpacpi_wlsw_emulstate; - printk(TPACPI_INFO - "radio switch emulation enabled\n"); + pr_info("radio switch emulation enabled\n"); } else #endif /* Not all thinkpads have a hardware radio switch */ if (acpi_evalf(hkey_handle, &status, "WLSW", "qd")) { tp_features.hotkey_wlsw = 1; radiosw_state = !!status; - printk(TPACPI_INFO - "radio switch found; radios are %s\n", + pr_info("radio switch found; radios are %s\n", enabled(status, 0)); } if (tp_features.hotkey_wlsw) @@ -3323,8 +3322,7 @@ static int __init hotkey_init(struct ibm_init_struct *iibm) if (!res && acpi_evalf(hkey_handle, &status, "MHKG", "qd")) { tp_features.hotkey_tablet = 1; tabletsw_state = !!(status & TP_HOTKEY_TABLET_MASK); - printk(TPACPI_INFO - "possible tablet mode switch found; " + pr_info("possible tablet mode switch found; " "ThinkPad in %s mode\n", (tabletsw_state) ? "tablet" : "laptop"); res = add_to_attr_set(hotkey_dev_attributes, @@ -3339,27 +3337,22 @@ static int __init hotkey_init(struct ibm_init_struct *iibm) goto err_exit; /* Set up key map */ - hotkey_keycode_map = kmalloc(TPACPI_HOTKEY_MAP_SIZE, GFP_KERNEL); if (!hotkey_keycode_map) { - printk(TPACPI_ERR - "failed to allocate memory for key map\n"); + pr_err("failed to allocate memory for key map\n"); res = -ENOMEM; goto err_exit; } - if (thinkpad_id.vendor == PCI_VENDOR_ID_LENOVO) { - dbg_printk(TPACPI_DBG_INIT | TPACPI_DBG_HKEY, - "using Lenovo default hot key map\n"); - memcpy(hotkey_keycode_map, &lenovo_keycode_map, - TPACPI_HOTKEY_MAP_SIZE); - } else { - dbg_printk(TPACPI_DBG_INIT | TPACPI_DBG_HKEY, - "using IBM default hot key map\n"); - memcpy(hotkey_keycode_map, &ibm_keycode_map, - TPACPI_HOTKEY_MAP_SIZE); - } + keymap_id = tpacpi_check_quirks(tpacpi_keymap_qtable, + ARRAY_SIZE(tpacpi_keymap_qtable)); + BUG_ON(keymap_id >= ARRAY_SIZE(tpacpi_keymaps)); + dbg_printk(TPACPI_DBG_INIT | TPACPI_DBG_HKEY, + "using keymap number %lu\n", keymap_id); + + memcpy(hotkey_keycode_map, &tpacpi_keymaps[keymap_id], + TPACPI_HOTKEY_MAP_SIZE); input_set_capability(tpacpi_inputdev, EV_MSC, MSC_SCAN); tpacpi_inputdev->keycodesize = TPACPI_HOTKEY_MAP_TYPESIZE; @@ -3387,19 +3380,14 @@ static int __init hotkey_init(struct ibm_init_struct *iibm) } /* Do not issue duplicate brightness change events to - * userspace */ - if (!tp_features.bright_acpimode) - /* update bright_acpimode... */ - tpacpi_check_std_acpi_brightness_support(); - - if (tp_features.bright_acpimode && acpi_video_backlight_support()) { - printk(TPACPI_INFO - "This ThinkPad has standard ACPI backlight " - "brightness control, supported by the ACPI " - "video driver\n"); - printk(TPACPI_NOTICE - "Disabling thinkpad-acpi brightness events " - "by default...\n"); + * userspace. tpacpi_detect_brightness_capabilities() must have + * been called before this point */ + if (acpi_video_backlight_support()) { + 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 @@ -3441,11 +3429,6 @@ static int __init hotkey_init(struct ibm_init_struct *iibm) "initial masks: user=0x%08x, fw=0x%08x, poll=0x%08x\n", hotkey_user_mask, hotkey_acpi_mask, hotkey_source_mask); - dbg_printk(TPACPI_DBG_INIT | TPACPI_DBG_HKEY, - "legacy ibm/hotkey event reporting over procfs %s\n", - (hotkey_report_mode < 2) ? - "enabled" : "disabled"); - tpacpi_inputdev->open = &hotkey_inputdev_open; tpacpi_inputdev->close = &hotkey_inputdev_close; @@ -3460,6 +3443,106 @@ err_exit: return (res < 0)? res : 1; } +/* Thinkpad X1 Carbon support 5 modes including Home mode, Web browser + * mode, Web conference mode, Function mode and Lay-flat mode. + * We support Home mode and Function mode currently. + * + * Will consider support rest of modes in future. + * + */ +enum ADAPTIVE_KEY_MODE { + HOME_MODE, + WEB_BROWSER_MODE, + WEB_CONFERENCE_MODE, + FUNCTION_MODE, + LAYFLAT_MODE +}; + +const int adaptive_keyboard_modes[] = { + HOME_MODE, +/* WEB_BROWSER_MODE = 2, + WEB_CONFERENCE_MODE = 3, */ + FUNCTION_MODE +}; + +#define DFR_CHANGE_ROW 0x101 +#define DFR_SHOW_QUICKVIEW_ROW 0x102 + +/* press Fn key a while second, it will switch to Function Mode. Then + * release Fn key, previous mode be restored. + */ +static bool adaptive_keyboard_mode_is_saved; +static int adaptive_keyboard_prev_mode; + +static int adaptive_keyboard_get_next_mode(int mode) +{ + size_t i; + size_t max_mode = ARRAY_SIZE(adaptive_keyboard_modes) - 1; + + for (i = 0; i <= max_mode; i++) { + if (adaptive_keyboard_modes[i] == mode) + break; + } + + if (i >= max_mode) + i = 0; + else + i++; + + return adaptive_keyboard_modes[i]; +} + +static bool adaptive_keyboard_hotkey_notify_hotkey(unsigned int scancode) +{ + u32 current_mode = 0; + int new_mode = 0; + + switch (scancode) { + case DFR_CHANGE_ROW: + if (adaptive_keyboard_mode_is_saved) { + new_mode = adaptive_keyboard_prev_mode; + adaptive_keyboard_mode_is_saved = false; + } else { + if (!acpi_evalf( + hkey_handle, ¤t_mode, + "GTRW", "dd", 0)) { + pr_err("Cannot read adaptive keyboard mode\n"); + return false; + } else { + new_mode = adaptive_keyboard_get_next_mode( + current_mode); + } + } + + if (!acpi_evalf(hkey_handle, NULL, "STRW", "vd", new_mode)) { + pr_err("Cannot set adaptive keyboard mode\n"); + return false; + } + + return true; + + case DFR_SHOW_QUICKVIEW_ROW: + if (!acpi_evalf(hkey_handle, + &adaptive_keyboard_prev_mode, + "GTRW", "dd", 0)) { + pr_err("Cannot read adaptive keyboard mode\n"); + return false; + } else { + adaptive_keyboard_mode_is_saved = true; + + if (!acpi_evalf(hkey_handle, + NULL, "STRW", "vd", FUNCTION_MODE)) { + pr_err("Cannot set adaptive keyboard mode\n"); + return false; + } + } + return true; + + default: + return false; + } +} + static bool hotkey_notify_hotkey(const u32 hkey, bool *send_acpi_ev, bool *ignore_acpi_ev) @@ -3469,7 +3552,8 @@ static bool hotkey_notify_hotkey(const u32 hkey, *send_acpi_ev = true; *ignore_acpi_ev = false; - if (scancode > 0 && scancode < 0x21) { + /* HKEY event 0x1001 is scancode 0x00 */ + if (scancode > 0 && scancode <= TPACPI_HOTKEY_MAP_LEN) { scancode--; if (!(hotkey_source_mask & (1 << scancode))) { tpacpi_input_send_key_masked(scancode); @@ -3478,6 +3562,8 @@ static bool hotkey_notify_hotkey(const u32 hkey, *ignore_acpi_ev = true; } return true; + } else { + return adaptive_keyboard_hotkey_notify_hotkey(scancode); } return false; } @@ -3505,8 +3591,7 @@ static bool hotkey_notify_wakeup(const u32 hkey, case TP_HKEY_EV_WKUP_S3_BATLOW: /* Battery on critical low level/S3 */ case TP_HKEY_EV_WKUP_S4_BATLOW: /* Battery on critical low level/S4 */ - printk(TPACPI_ALERT - "EMERGENCY WAKEUP: battery almost empty\n"); + pr_alert("EMERGENCY WAKEUP: battery almost empty\n"); /* how to auto-heal: */ /* 2313: woke up from S3, go to S4/S5 */ /* 2413: woke up from S4, go to S5 */ @@ -3517,14 +3602,40 @@ static bool hotkey_notify_wakeup(const u32 hkey, } if (hotkey_wakeup_reason != TP_ACPI_WAKEUP_NONE) { - printk(TPACPI_INFO - "woke up due to a hot-unplug " - "request...\n"); + pr_info("woke up due to a hot-unplug request...\n"); hotkey_wakeup_reason_notify_change(); } return true; } +static bool hotkey_notify_dockevent(const u32 hkey, + bool *send_acpi_ev, + bool *ignore_acpi_ev) +{ + /* 0x4000-0x4FFF: dock-related events */ + *send_acpi_ev = true; + *ignore_acpi_ev = false; + + switch (hkey) { + case TP_HKEY_EV_UNDOCK_ACK: + /* ACPI undock operation completed after wakeup */ + hotkey_autosleep_ack = 1; + pr_info("undocked\n"); + hotkey_wakeup_hotunplug_complete_notify_change(); + return true; + + case TP_HKEY_EV_HOTPLUG_DOCK: /* docked to port replicator */ + pr_info("docked into hotplug port replicator\n"); + return true; + case TP_HKEY_EV_HOTPLUG_UNDOCK: /* undocked from port replicator */ + pr_info("undocked from hotplug port replicator\n"); + return true; + + default: + return false; + } +} + static bool hotkey_notify_usrevent(const u32 hkey, bool *send_acpi_ev, bool *ignore_acpi_ev) @@ -3559,49 +3670,58 @@ static bool hotkey_notify_usrevent(const u32 hkey, static void thermal_dump_all_sensors(void); -static bool hotkey_notify_thermal(const u32 hkey, +static bool hotkey_notify_6xxx(const u32 hkey, bool *send_acpi_ev, bool *ignore_acpi_ev) { bool known = true; - /* 0x6000-0x6FFF: thermal alarms */ + /* 0x6000-0x6FFF: thermal alarms/notices and keyboard events */ *send_acpi_ev = true; *ignore_acpi_ev = false; switch (hkey) { case TP_HKEY_EV_THM_TABLE_CHANGED: - printk(TPACPI_INFO - "EC reports that Thermal Table has changed\n"); + pr_info("EC reports that Thermal Table has changed\n"); /* recommended action: do nothing, we don't have * Lenovo ATM information */ return true; case TP_HKEY_EV_ALARM_BAT_HOT: - printk(TPACPI_CRIT - "THERMAL ALARM: battery is too hot!\n"); + pr_crit("THERMAL ALARM: battery is too hot!\n"); /* recommended action: warn user through gui */ break; case TP_HKEY_EV_ALARM_BAT_XHOT: - printk(TPACPI_ALERT - "THERMAL EMERGENCY: battery is extremely hot!\n"); + pr_alert("THERMAL EMERGENCY: battery is extremely hot!\n"); /* recommended action: immediate sleep/hibernate */ break; case TP_HKEY_EV_ALARM_SENSOR_HOT: - printk(TPACPI_CRIT - "THERMAL ALARM: " + 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: - printk(TPACPI_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: + /* X120e, X121e, X220, X220i, X220t, X230, T420, T420s, W520: + * AC status changed; can be triggered by plugging or + * unplugging AC adapter, docking or undocking. */ + + /* fallthrough */ + + case TP_HKEY_EV_KEY_NUMLOCK: + case TP_HKEY_EV_KEY_FN: + /* key press events, we just ignore them as long as the EC + * is still reporting them in the normal keyboard stream */ + *send_acpi_ev = false; + *ignore_acpi_ev = true; + return true; + default: - printk(TPACPI_ALERT - "THERMAL ALERT: unknown thermal alarm received\n"); + pr_warn("unknown possible thermal alarm or keyboard event received\n"); known = false; } @@ -3618,8 +3738,7 @@ static void hotkey_notify(struct ibm_struct *ibm, u32 event) bool known_ev; if (event != 0x80) { - printk(TPACPI_ERR - "unknown HKEY notification event %d\n", event); + pr_err("unknown HKEY notification event %d\n", event); /* forward it to userspace, maybe it knows how to handle it */ acpi_bus_generate_netlink_event( ibm->acpi->device->pnp.device_class, @@ -3630,7 +3749,7 @@ static void hotkey_notify(struct ibm_struct *ibm, u32 event) while (1) { if (!acpi_evalf(hkey_handle, &hkey, "MHKP", "d")) { - printk(TPACPI_ERR "failed to retrieve HKEY event\n"); + pr_err("failed to retrieve HKEY event\n"); return; } @@ -3655,27 +3774,25 @@ static void hotkey_notify(struct ibm_struct *ibm, u32 event) break; case 3: /* 0x3000-0x3FFF: bay-related wakeups */ - if (hkey == TP_HKEY_EV_BAYEJ_ACK) { + switch (hkey) { + case TP_HKEY_EV_BAYEJ_ACK: hotkey_autosleep_ack = 1; - printk(TPACPI_INFO - "bay ejected\n"); + pr_info("bay ejected\n"); hotkey_wakeup_hotunplug_complete_notify_change(); known_ev = true; - } else { + break; + case TP_HKEY_EV_OPTDRV_EJ: + /* FIXME: kick libata if SATA link offline */ + known_ev = true; + break; + default: known_ev = false; } break; case 4: - /* 0x4000-0x4FFF: dock-related wakeups */ - if (hkey == TP_HKEY_EV_UNDOCK_ACK) { - hotkey_autosleep_ack = 1; - printk(TPACPI_INFO - "undocked\n"); - hotkey_wakeup_hotunplug_complete_notify_change(); - known_ev = true; - } else { - known_ev = false; - } + /* 0x4000-0x4FFF: dock-related events */ + known_ev = hotkey_notify_dockevent(hkey, &send_acpi_ev, + &ignore_acpi_ev); break; case 5: /* 0x5000-0x5FFF: human interface helpers */ @@ -3683,8 +3800,9 @@ static void hotkey_notify(struct ibm_struct *ibm, u32 event) &ignore_acpi_ev); break; case 6: - /* 0x6000-0x6FFF: thermal alarms */ - known_ev = hotkey_notify_thermal(hkey, &send_acpi_ev, + /* 0x6000-0x6FFF: thermal alarms/notices and + * keyboard events */ + known_ev = hotkey_notify_6xxx(hkey, &send_acpi_ev, &ignore_acpi_ev); break; case 7: @@ -3701,18 +3819,9 @@ static void hotkey_notify(struct ibm_struct *ibm, u32 event) known_ev = false; } if (!known_ev) { - printk(TPACPI_NOTICE - "unhandled HKEY event 0x%04x\n", hkey); - printk(TPACPI_NOTICE - "please report the conditions when this " - "event happened to %s\n", TPACPI_MAIL); - } - - /* Legacy events */ - if (!ignore_acpi_ev && - (send_acpi_ev || hotkey_report_mode < 2)) { - acpi_bus_generate_proc_event(ibm->acpi->device, - event, hkey); + pr_notice("unhandled HKEY event 0x%04x\n", hkey); + pr_notice("please report the conditions when this " + "event happened to %s\n", TPACPI_MAIL); } /* netlink events */ @@ -3725,21 +3834,35 @@ static void hotkey_notify(struct ibm_struct *ibm, u32 event) } } -static void hotkey_suspend(pm_message_t state) +static void hotkey_suspend(void) { + int hkeyv; + /* Do these on suspend, we get the events on early resume! */ hotkey_wakeup_reason = TP_ACPI_WAKEUP_NONE; hotkey_autosleep_ack = 0; + + /* save previous mode of adaptive keyboard of X1 Carbon */ + if (acpi_evalf(hkey_handle, &hkeyv, "MHKV", "qd")) { + if ((hkeyv >> 8) == 2) { + if (!acpi_evalf(hkey_handle, + &adaptive_keyboard_prev_mode, + "GTRW", "dd", 0)) { + pr_err("Cannot read adaptive keyboard mode.\n"); + } + } + } } static void hotkey_resume(void) { + int hkeyv; + tpacpi_disable_brightness_delay(); if (hotkey_status_set(true) < 0 || hotkey_mask_set(hotkey_acpi_mask) < 0) - printk(TPACPI_ERR - "error while attempting to reset the event " + pr_err("error while attempting to reset the event " "firmware interface\n"); tpacpi_send_radiosw_update(); @@ -3747,6 +3870,18 @@ static void hotkey_resume(void) hotkey_wakeup_reason_notify_change(); hotkey_wakeup_hotunplug_complete_notify_change(); hotkey_poll_setup_safe(false); + + /* restore previous mode of adapive keyboard of X1 Carbon */ + if (acpi_evalf(hkey_handle, &hkeyv, "MHKV", "qd")) { + if ((hkeyv >> 8) == 2) { + if (!acpi_evalf(hkey_handle, + NULL, + "STRW", "vd", + adaptive_keyboard_prev_mode)) { + pr_err("Cannot set adaptive keyboard mode.\n"); + } + } + } } /* procfs -------------------------------------------------------------- */ @@ -3784,14 +3919,12 @@ static void hotkey_enabledisable_warn(bool enable) { tpacpi_log_usertask("procfs hotkey enable/disable"); if (!WARN((tpacpi_lifecycle == TPACPI_LIFE_RUNNING || !enable), - TPACPI_WARN - "hotkey enable/disable functionality has been " - "removed from the driver. Hotkeys are always " - "enabled\n")) - printk(TPACPI_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) @@ -3840,7 +3973,8 @@ errexit: } static const struct acpi_device_id ibm_htk_device_ids[] = { - {TPACPI_ACPI_HKEY_HID, 0}, + {TPACPI_ACPI_IBM_HKEY_HID, 0}, + {TPACPI_ACPI_LENOVO_HKEY_HID, 0}, {"", 0}, }; @@ -3870,7 +4004,7 @@ enum { TP_ACPI_BLUETOOTH_HWPRESENT = 0x01, /* Bluetooth hw available */ TP_ACPI_BLUETOOTH_RADIOSSW = 0x02, /* Bluetooth radio enabled */ TP_ACPI_BLUETOOTH_RESUMECTRL = 0x04, /* Bluetooth state at resume: - off / last state */ + 0 = disable, 1 = enable */ }; enum { @@ -3916,10 +4050,11 @@ static int bluetooth_set_status(enum tpacpi_rfkill_state state) } #endif - /* We make sure to keep TP_ACPI_BLUETOOTH_RESUMECTRL off */ - status = TP_ACPI_BLUETOOTH_RESUMECTRL; if (state == TPACPI_RFK_RADIO_ON) - status |= TP_ACPI_BLUETOOTH_RADIOSSW; + status = TP_ACPI_BLUETOOTH_RADIOSSW + | TP_ACPI_BLUETOOTH_RESUMECTRL; + else + status = 0; if (!acpi_evalf(hkey_handle, NULL, "SBDC", "vd", status)) return -EIO; @@ -3969,11 +4104,10 @@ static void bluetooth_shutdown(void) /* Order firmware to save current state to NVRAM */ if (!acpi_evalf(NULL, NULL, "\\BLTH", "vd", TP_ACPI_BLTH_SAVE_STATE)) - printk(TPACPI_NOTICE - "failed to save bluetooth state to NVRAM\n"); + pr_notice("failed to save bluetooth state to NVRAM\n"); else vdbg_printk(TPACPI_DBG_RFKILL, - "bluestooth state saved to NVRAM\n"); + "bluetooth state saved to NVRAM\n"); } static void bluetooth_exit(void) @@ -4009,8 +4143,7 @@ static int __init bluetooth_init(struct ibm_init_struct *iibm) #ifdef CONFIG_THINKPAD_ACPI_DEBUGFACILITIES if (dbg_bluetoothemul) { tp_features.bluetooth = 1; - printk(TPACPI_INFO - "bluetooth switch emulation enabled\n"); + pr_info("bluetooth switch emulation enabled\n"); } else #endif if (tp_features.bluetooth && @@ -4070,7 +4203,7 @@ enum { TP_ACPI_WANCARD_HWPRESENT = 0x01, /* Wan hw available */ TP_ACPI_WANCARD_RADIOSSW = 0x02, /* Wan radio enabled */ TP_ACPI_WANCARD_RESUMECTRL = 0x04, /* Wan state at resume: - off / last state */ + 0 = disable, 1 = enable */ }; #define TPACPI_RFK_WWAN_SW_NAME "tpacpi_wwan_sw" @@ -4107,10 +4240,11 @@ static int wan_set_status(enum tpacpi_rfkill_state state) } #endif - /* We make sure to set TP_ACPI_WANCARD_RESUMECTRL */ - status = TP_ACPI_WANCARD_RESUMECTRL; if (state == TPACPI_RFK_RADIO_ON) - status |= TP_ACPI_WANCARD_RADIOSSW; + status = TP_ACPI_WANCARD_RADIOSSW + | TP_ACPI_WANCARD_RESUMECTRL; + else + status = 0; if (!acpi_evalf(hkey_handle, NULL, "SWAN", "vd", status)) return -EIO; @@ -4160,8 +4294,7 @@ static void wan_shutdown(void) /* Order firmware to save current state to NVRAM */ if (!acpi_evalf(NULL, NULL, "\\WGSV", "vd", TP_ACPI_WGSV_SAVE_STATE)) - printk(TPACPI_NOTICE - "failed to save WWAN state to NVRAM\n"); + pr_notice("failed to save WWAN state to NVRAM\n"); else vdbg_printk(TPACPI_DBG_RFKILL, "WWAN state saved to NVRAM\n"); @@ -4198,8 +4331,7 @@ static int __init wan_init(struct ibm_init_struct *iibm) #ifdef CONFIG_THINKPAD_ACPI_DEBUGFACILITIES if (dbg_wwanemul) { tp_features.wan = 1; - printk(TPACPI_INFO - "wwan switch emulation enabled\n"); + pr_info("wwan switch emulation enabled\n"); } else #endif if (tp_features.wan && @@ -4339,8 +4471,7 @@ static int __init uwb_init(struct ibm_init_struct *iibm) #ifdef CONFIG_THINKPAD_ACPI_DEBUGFACILITIES if (dbg_uwbemul) { tp_features.uwb = 1; - printk(TPACPI_INFO - "uwb switch emulation enabled\n"); + pr_info("uwb switch emulation enabled\n"); } else #endif if (tp_features.uwb && @@ -4401,6 +4532,15 @@ static int video_orig_autosw; static int video_autosw_get(void); static int video_autosw_set(int enable); +TPACPI_HANDLE(vid, root, + "\\_SB.PCI.AGP.VGA", /* 570 */ + "\\_SB.PCI0.AGP0.VID0", /* 600e/x, 770x */ + "\\_SB.PCI0.VID0", /* 770e */ + "\\_SB.PCI0.VID", /* A21e, G4x, R50e, X30, X40 */ + "\\_SB.PCI0.AGP.VGA", /* X100e and a few others */ + "\\_SB.PCI0.AGP.VID", /* all others */ + ); /* R30, R31 */ + TPACPI_HANDLE(vid2, root, "\\_SB.PCI0.AGPB.VID"); /* G41 */ static int __init video_init(struct ibm_init_struct *iibm) @@ -4410,7 +4550,8 @@ static int __init video_init(struct ibm_init_struct *iibm) vdbg_printk(TPACPI_DBG_INIT, "initializing video subdriver\n"); TPACPI_ACPIHANDLE_INIT(vid); - TPACPI_ACPIHANDLE_INIT(vid2); + if (tpacpi_is_ibm()) + TPACPI_ACPIHANDLE_INIT(vid2); if (vid2_handle && acpi_evalf(NULL, &ivga, "\\IVGA", "d") && ivga) /* G41, assume IVGA doesn't change */ @@ -4419,10 +4560,12 @@ static int __init video_init(struct ibm_init_struct *iibm) if (!vid_handle) /* video switching not supported on R30, R31 */ video_supported = TPACPI_VIDEO_NONE; - else if (acpi_evalf(vid_handle, &video_orig_autosw, "SWIT", "qd")) + else if (tpacpi_is_ibm() && + acpi_evalf(vid_handle, &video_orig_autosw, "SWIT", "qd")) /* 570 */ video_supported = TPACPI_VIDEO_570; - else if (acpi_evalf(vid_handle, &video_orig_autosw, "^VADL", "qd")) + else if (tpacpi_is_ibm() && + acpi_evalf(vid_handle, &video_orig_autosw, "^VADL", "qd")) /* 600e/x, 770e, 770x */ video_supported = TPACPI_VIDEO_770; else @@ -4441,7 +4584,7 @@ static void video_exit(void) dbg_printk(TPACPI_DBG_EXIT, "restoring original video autoswitch mode\n"); if (video_autosw_set(video_orig_autosw)) - printk(TPACPI_ERR "error while trying to restore original " + pr_err("error while trying to restore original " "video autoswitch mode\n"); } @@ -4514,8 +4657,7 @@ static int video_outputsw_set(int status) res = acpi_evalf(vid_handle, NULL, "ASWT", "vdd", status * 0x100, 0); if (!autosw && video_autosw_set(autosw)) { - printk(TPACPI_ERR - "video auto-switch left enabled due to error\n"); + pr_err("video auto-switch left enabled due to error\n"); return -EIO; } break; @@ -4584,8 +4726,7 @@ static int video_outputsw_cycle(void) return -ENOSYS; } if (!autosw && video_autosw_set(autosw)) { - printk(TPACPI_ERR - "video auto-switch left enabled due to error\n"); + pr_err("video auto-switch left enabled due to error\n"); return -EIO; } @@ -4619,6 +4760,10 @@ static int video_read(struct seq_file *m) return 0; } + /* Even reads can crash X.org, so... */ + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + status = video_outputsw_get(); if (status < 0) return status; @@ -4652,6 +4797,10 @@ static int video_write(char *buf) if (video_supported == TPACPI_VIDEO_NONE) return -ENODEV; + /* Even reads can crash X.org, let alone writes... */ + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + enable = 0; disable = 0; @@ -4791,8 +4940,10 @@ static int __init light_init(struct ibm_init_struct *iibm) vdbg_printk(TPACPI_DBG_INIT, "initializing light subdriver\n"); - TPACPI_ACPIHANDLE_INIT(ledb); - TPACPI_ACPIHANDLE_INIT(lght); + if (tpacpi_is_ibm()) { + TPACPI_ACPIHANDLE_INIT(ledb); + TPACPI_ACPIHANDLE_INIT(lght); + } TPACPI_ACPIHANDLE_INIT(cmos); INIT_WORK(&tpacpi_led_thinklight.work, light_set_status_worker); @@ -4828,8 +4979,7 @@ static int __init light_init(struct ibm_init_struct *iibm) static void light_exit(void) { led_classdev_unregister(&tpacpi_led_thinklight.led_classdev); - if (work_pending(&tpacpi_led_thinklight.work)) - flush_workqueue(tpacpi_wq); + flush_workqueue(tpacpi_wq); } static int light_read(struct seq_file *m) @@ -4987,11 +5137,7 @@ enum { /* For TPACPI_LED_OLD */ static enum led_access_mode led_supported; -TPACPI_HANDLE(led, ec, "SLED", /* 570 */ - "SYSL", /* 600e/x, 770e, 770x, A21e, A2xm/p, */ - /* T20-22, X20-21 */ - "LED", /* all others */ - ); /* R30, R31 */ +static acpi_handle led_handle; #define TPACPI_LED_NUMLEDS 16 static struct tpacpi_led_classdev *tpacpi_leds; @@ -5172,6 +5318,7 @@ static void led_exit(void) led_classdev_unregister(&tpacpi_leds[i].led_classdev); } + flush_workqueue(tpacpi_wq); kfree(tpacpi_leds); } @@ -5251,6 +5398,32 @@ static const struct tpacpi_quirk led_useful_qtable[] __initconst = { #undef TPACPI_LEDQ_IBM #undef TPACPI_LEDQ_LNV +static enum led_access_mode __init led_init_detect_mode(void) +{ + acpi_status status; + + if (tpacpi_is_ibm()) { + /* 570 */ + status = acpi_get_handle(ec_handle, "SLED", &led_handle); + if (ACPI_SUCCESS(status)) + return TPACPI_LED_570; + + /* 600e/x, 770e, 770x, A21e, A2xm/p, T20-22, X20-21 */ + status = acpi_get_handle(ec_handle, "SYSL", &led_handle); + if (ACPI_SUCCESS(status)) + return TPACPI_LED_OLD; + } + + /* most others */ + status = acpi_get_handle(ec_handle, "LED", &led_handle); + if (ACPI_SUCCESS(status)) + return TPACPI_LED_NEW; + + /* R30, R31, and unknown firmwares */ + led_handle = NULL; + return TPACPI_LED_NONE; +} + static int __init led_init(struct ibm_init_struct *iibm) { unsigned int i; @@ -5259,20 +5432,17 @@ static int __init led_init(struct ibm_init_struct *iibm) vdbg_printk(TPACPI_DBG_INIT, "initializing LED subdriver\n"); - TPACPI_ACPIHANDLE_INIT(led); + led_supported = led_init_detect_mode(); - if (!led_handle) - /* led not supported on R30, R31 */ - led_supported = TPACPI_LED_NONE; - else if (strlencmp(led_path, "SLED") == 0) - /* 570 */ - led_supported = TPACPI_LED_570; - else if (strlencmp(led_path, "SYSL") == 0) - /* 600e/x, 770e, 770x, A21e, A2xm/p, T20-22, X20-21 */ - led_supported = TPACPI_LED_OLD; - else - /* all others */ - led_supported = TPACPI_LED_NEW; + if (led_supported != TPACPI_LED_NONE) { + useful_leds = tpacpi_check_quirks(led_useful_qtable, + ARRAY_SIZE(led_useful_qtable)); + + if (!useful_leds) { + led_handle = NULL; + led_supported = TPACPI_LED_NONE; + } + } vdbg_printk(TPACPI_DBG_INIT, "LED commands are %s, mode %d\n", str_supported(led_supported), led_supported); @@ -5283,14 +5453,13 @@ static int __init led_init(struct ibm_init_struct *iibm) tpacpi_leds = kzalloc(sizeof(*tpacpi_leds) * TPACPI_LED_NUMLEDS, GFP_KERNEL); if (!tpacpi_leds) { - printk(TPACPI_ERR "Out of memory for LED data\n"); + pr_err("Out of memory for LED data\n"); return -ENOMEM; } - useful_leds = tpacpi_check_quirks(led_useful_qtable, - ARRAY_SIZE(led_useful_qtable)); - for (i = 0; i < TPACPI_LED_NUMLEDS; i++) { + tpacpi_leds[i].led = -1; + if (!tpacpi_is_led_restricted(i) && test_bit(i, &useful_leds)) { rc = tpacpi_init_led(i); @@ -5302,9 +5471,8 @@ static int __init led_init(struct ibm_init_struct *iibm) } #ifdef CONFIG_THINKPAD_ACPI_UNSAFE_LEDS - printk(TPACPI_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; } @@ -5349,9 +5517,13 @@ static int led_write(char *buf) return -ENODEV; while ((cmd = next_cmd(&buf))) { - if (sscanf(cmd, "%d", &led) != 1 || led < 0 || led > 15) + if (sscanf(cmd, "%d", &led) != 1) return -EINVAL; + if (led < 0 || led > (TPACPI_LED_NUMLEDS - 1) || + tpacpi_leds[led].led < 0) + return -ENODEV; + if (strstr(cmd, "off")) { s = TPACPI_LED_OFF; } else if (strstr(cmd, "on")) { @@ -5574,17 +5746,16 @@ static void thermal_dump_all_sensors(void) if (n <= 0) return; - printk(TPACPI_NOTICE - "temperatures (Celsius):"); + pr_notice("temperatures (Celsius):"); for (i = 0; i < n; i++) { if (t.temp[i] != TPACPI_THERMAL_SENSOR_NA) - printk(KERN_CONT " %d", (int)(t.temp[i] / 1000)); + pr_cont(" %d", (int)(t.temp[i] / 1000)); else - printk(KERN_CONT " N/A"); + pr_cont(" N/A"); } - printk(KERN_CONT "\n"); + pr_cont("\n"); } /* sysfs temp##_input -------------------------------------------------- */ @@ -5704,14 +5875,12 @@ 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) { - printk(TPACPI_ERR - "ThinkPad ACPI EC access misbehaving, " + pr_err("ThinkPad ACPI EC access misbehaving, " "falling back to ACPI TMPx access " "mode\n"); thermal_read_mode = TPACPI_THERMAL_ACPI_TMP07; } else { - printk(TPACPI_ERR - "ThinkPad ACPI EC access misbehaving, " + pr_err("ThinkPad ACPI EC access misbehaving, " "disabling thermal sensors access\n"); thermal_read_mode = TPACPI_THERMAL_NONE; } @@ -5721,11 +5890,12 @@ static int __init thermal_init(struct ibm_init_struct *iibm) TPACPI_THERMAL_TPEC_16 : TPACPI_THERMAL_TPEC_8; } } else if (acpi_tmp7) { - if (acpi_evalf(ec_handle, NULL, "UPDT", "qv")) { + if (tpacpi_is_ibm() && + acpi_evalf(ec_handle, NULL, "UPDT", "qv")) { /* 600e/x, 770e, 770x */ thermal_read_mode = TPACPI_THERMAL_ACPI_UPDT; } else { - /* Standard ACPI TMPx access, max 8 sensors */ + /* IBM/LENOVO DSDT EC.TMPx access, max 8 sensors */ thermal_read_mode = TPACPI_THERMAL_ACPI_TMP07; } } else { @@ -5771,7 +5941,7 @@ static void thermal_exit(void) case TPACPI_THERMAL_ACPI_TMP07: case TPACPI_THERMAL_ACPI_UPDT: sysfs_remove_group(&tpacpi_sensors_pdev->dev.kobj, - &thermal_temp_input16_group); + &thermal_temp_input8_group); break; case TPACPI_THERMAL_NONE: default: @@ -5807,75 +5977,6 @@ static struct ibm_struct thermal_driver_data = { }; /************************************************************************* - * EC Dump subdriver - */ - -static u8 ecdump_regs[256]; - -static int ecdump_read(struct seq_file *m) -{ - int i, j; - u8 v; - - seq_printf(m, "EC " - " +00 +01 +02 +03 +04 +05 +06 +07" - " +08 +09 +0a +0b +0c +0d +0e +0f\n"); - for (i = 0; i < 256; i += 16) { - seq_printf(m, "EC 0x%02x:", i); - for (j = 0; j < 16; j++) { - if (!acpi_ec_read(i + j, &v)) - break; - if (v != ecdump_regs[i + j]) - seq_printf(m, " *%02x", v); - else - seq_printf(m, " %02x", v); - ecdump_regs[i + j] = v; - } - seq_putc(m, '\n'); - if (j != 16) - break; - } - - /* These are way too dangerous to advertise openly... */ -#if 0 - seq_printf(m, "commands:\t0x<offset> 0x<value>" - " (<offset> is 00-ff, <value> is 00-ff)\n"); - seq_printf(m, "commands:\t0x<offset> <value> " - " (<offset> is 00-ff, <value> is 0-255)\n"); -#endif - return 0; -} - -static int ecdump_write(char *buf) -{ - char *cmd; - int i, v; - - while ((cmd = next_cmd(&buf))) { - if (sscanf(cmd, "0x%x 0x%x", &i, &v) == 2) { - /* i and v set */ - } else if (sscanf(cmd, "0x%x %u", &i, &v) == 2) { - /* i and v set */ - } else - return -EINVAL; - if (i >= 0 && i < 256 && v >= 0 && v < 256) { - if (!acpi_ec_write(i, v)) - return -EIO; - } else - return -EINVAL; - } - - return 0; -} - -static struct ibm_struct ecdump_driver_data = { - .name = "ecdump", - .read = ecdump_read, - .write = ecdump_write, - .flags.experimental = 1, -}; - -/************************************************************************* * Backlight/brightness subdriver */ @@ -5934,7 +6035,7 @@ static unsigned int tpacpi_brightness_nvram_get(void) lnvram = (nvram_read_byte(TP_NVRAM_ADDR_BRIGHTNESS) & TP_NVRAM_MASK_LEVEL_BRIGHTNESS) >> TP_NVRAM_POS_LEVEL_BRIGHTNESS; - lnvram &= (tp_features.bright_16levels) ? 0x0f : 0x07; + lnvram &= bright_maxlvl; return lnvram; } @@ -6043,8 +6144,7 @@ static int brightness_set(unsigned int value) { int res; - if (value > ((tp_features.bright_16levels)? 15 : 7) || - value < 0) + if (value > bright_maxlvl || value < 0) return -EINVAL; vdbg_printk(TPACPI_DBG_BRGHT, @@ -6112,7 +6212,7 @@ static void tpacpi_brightness_notify_change(void) BACKLIGHT_UPDATE_HOTKEY); } -static struct backlight_ops ibm_backlight_data = { +static const struct backlight_ops ibm_backlight_data = { .get_brightness = brightness_get, .update_status = brightness_update_status, }; @@ -6120,6 +6220,61 @@ static struct backlight_ops ibm_backlight_data = { /* --------------------------------------------------------------------- */ /* + * Call _BCL method of video device. On some ThinkPads this will + * switch the firmware to the ACPI brightness control mode. + */ + +static int __init tpacpi_query_bcl_levels(acpi_handle handle) +{ + struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; + union acpi_object *obj; + struct acpi_device *device, *child; + int rc; + + if (acpi_bus_get_device(handle, &device)) + return 0; + + rc = 0; + list_for_each_entry(child, &device->children, node) { + acpi_status status = acpi_evaluate_object(child->handle, "_BCL", + NULL, &buffer); + if (ACPI_FAILURE(status)) + continue; + + obj = (union acpi_object *)buffer.pointer; + if (!obj || (obj->type != ACPI_TYPE_PACKAGE)) { + pr_err("Unknown _BCL data, please report this to %s\n", + TPACPI_MAIL); + rc = 0; + } else { + rc = obj->package.count; + } + break; + } + + kfree(buffer.pointer); + return rc; +} + + +/* + * Returns 0 (no ACPI _BCL or _BCL invalid), or size of brightness map + */ +static unsigned int __init tpacpi_check_std_acpi_brightness_support(void) +{ + acpi_handle video_device; + int bcl_levels = 0; + + tpacpi_acpi_handle_locate("video", NULL, &video_device); + if (video_device) + bcl_levels = tpacpi_query_bcl_levels(video_device); + + tp_features.bright_acpimode = (bcl_levels > 0); + + return (bcl_levels > 2) ? (bcl_levels - 2) : 0; +} + +/* * These are only useful for models that have only one possibility * of GPU. If the BIOS model handles both ATI and Intel, don't use * these quirks. @@ -6133,13 +6288,13 @@ static const struct tpacpi_quirk brightness_quirk_table[] __initconst = { TPACPI_Q_IBM('1', 'Y', TPACPI_BRGHT_Q_EC), /* T43/p ATI */ /* Models with ATI GPUs that can use ECNVRAM */ - TPACPI_Q_IBM('1', 'R', TPACPI_BRGHT_Q_EC), + TPACPI_Q_IBM('1', 'R', TPACPI_BRGHT_Q_EC), /* R50,51 T40-42 */ TPACPI_Q_IBM('1', 'Q', TPACPI_BRGHT_Q_ASK|TPACPI_BRGHT_Q_EC), - TPACPI_Q_IBM('7', '6', TPACPI_BRGHT_Q_ASK|TPACPI_BRGHT_Q_EC), + TPACPI_Q_IBM('7', '6', TPACPI_BRGHT_Q_EC), /* R52 */ TPACPI_Q_IBM('7', '8', TPACPI_BRGHT_Q_ASK|TPACPI_BRGHT_Q_EC), /* Models with Intel Extreme Graphics 2 */ - TPACPI_Q_IBM('1', 'U', TPACPI_BRGHT_Q_NOEC), + TPACPI_Q_IBM('1', 'U', TPACPI_BRGHT_Q_NOEC), /* X40 */ TPACPI_Q_IBM('1', 'V', TPACPI_BRGHT_Q_ASK|TPACPI_BRGHT_Q_EC), TPACPI_Q_IBM('1', 'W', TPACPI_BRGHT_Q_ASK|TPACPI_BRGHT_Q_EC), @@ -6149,8 +6304,47 @@ static const struct tpacpi_quirk brightness_quirk_table[] __initconst = { TPACPI_Q_IBM('7', '5', TPACPI_BRGHT_Q_NOEC), /* X41 Tablet */ }; +/* + * Returns < 0 for error, otherwise sets tp_features.bright_* + * and bright_maxlvl. + */ +static void __init tpacpi_detect_brightness_capabilities(void) +{ + unsigned int b; + + vdbg_printk(TPACPI_DBG_INIT, + "detecting firmware brightness interface capabilities\n"); + + /* we could run a quirks check here (same table used by + * brightness_init) if needed */ + + /* + * We always attempt to detect acpi support, so as to switch + * Lenovo Vista BIOS to ACPI brightness mode even if we are not + * going to publish a backlight interface + */ + b = tpacpi_check_std_acpi_brightness_support(); + switch (b) { + case 16: + bright_maxlvl = 15; + pr_info("detected a 16-level brightness capable ThinkPad\n"); + break; + case 8: + case 0: + bright_maxlvl = 7; + pr_info("detected a 8-level brightness capable ThinkPad\n"); + break; + default: + pr_err("Unsupported brightness interface, " + "please contact %s\n", TPACPI_MAIL); + tp_features.bright_unkfw = 1; + bright_maxlvl = b - 1; + } +} + static int __init brightness_init(struct ibm_init_struct *iibm) { + struct backlight_properties props; int b; unsigned long quirks; @@ -6161,34 +6355,11 @@ static int __init brightness_init(struct ibm_init_struct *iibm) quirks = tpacpi_check_quirks(brightness_quirk_table, ARRAY_SIZE(brightness_quirk_table)); - /* - * We always attempt to detect acpi support, so as to switch - * Lenovo Vista BIOS to ACPI brightness mode even if we are not - * going to publish a backlight interface - */ - b = tpacpi_check_std_acpi_brightness_support(); - if (b > 0) { - - if (acpi_video_backlight_support()) { - if (brightness_enable > 1) { - printk(TPACPI_NOTICE - "Standard ACPI backlight interface " - "available, not loading native one.\n"); - return 1; - } else if (brightness_enable == 1) { - printk(TPACPI_NOTICE - "Backlight control force enabled, even if standard " - "ACPI backlight interface is available\n"); - } - } else { - if (brightness_enable > 1) { - printk(TPACPI_NOTICE - "Standard ACPI backlight interface not " - "available, thinkpad_acpi native " - "brightness control enabled\n"); - } - } - } + /* tpacpi_detect_brightness_capabilities() must have run already */ + + /* if it is unknown, we don't handle it: it wouldn't be safe */ + if (tp_features.bright_unkfw) + return 1; if (!brightness_enable) { dbg_printk(TPACPI_DBG_INIT | TPACPI_DBG_BRGHT, @@ -6197,14 +6368,22 @@ static int __init brightness_init(struct ibm_init_struct *iibm) return 1; } - if (b > 16) { - printk(TPACPI_ERR - "Unsupported brightness interface, " - "please contact %s\n", TPACPI_MAIL); - return 1; + if (acpi_video_backlight_support()) { + if (brightness_enable > 1) { + 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"); + 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"); } - if (b == 16) - tp_features.bright_16levels = 1; /* * Check for module parameter bogosity, note that we @@ -6228,7 +6407,7 @@ static int __init brightness_init(struct ibm_init_struct *iibm) } /* Safety */ - if (thinkpad_id.vendor != PCI_VENDOR_ID_IBM && + if (!tpacpi_is_ibm() && (brightness_mode == TPACPI_BRGHT_MODE_ECNVRAM || brightness_mode == TPACPI_BRGHT_MODE_EC)) return -EINVAL; @@ -6236,34 +6415,34 @@ static int __init brightness_init(struct ibm_init_struct *iibm) if (tpacpi_brightness_get_raw(&b) < 0) return 1; - if (tp_features.bright_16levels) - printk(TPACPI_INFO - "detected a 16-level brightness capable ThinkPad\n"); - - ibm_backlight_device = backlight_device_register( - TPACPI_BACKLIGHT_DEV_NAME, NULL, NULL, - &ibm_backlight_data); + memset(&props, 0, sizeof(struct backlight_properties)); + props.type = BACKLIGHT_PLATFORM; + props.max_brightness = bright_maxlvl; + props.brightness = b & TP_EC_BACKLIGHT_LVLMSK; + ibm_backlight_device = backlight_device_register(TPACPI_BACKLIGHT_DEV_NAME, + NULL, NULL, + &ibm_backlight_data, + &props); if (IS_ERR(ibm_backlight_device)) { int rc = PTR_ERR(ibm_backlight_device); ibm_backlight_device = NULL; - printk(TPACPI_ERR "Could not register backlight device\n"); + pr_err("Could not register backlight device\n"); return rc; } vdbg_printk(TPACPI_DBG_INIT | TPACPI_DBG_BRGHT, "brightness is supported\n"); if (quirks & TPACPI_BRGHT_Q_ASK) { - printk(TPACPI_NOTICE - "brightness: will use unverified default: " - "brightness_mode=%d\n", brightness_mode); - printk(TPACPI_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); } - ibm_backlight_device->props.max_brightness = - (tp_features.bright_16levels)? 15 : 7; - ibm_backlight_device->props.brightness = b & TP_EC_BACKLIGHT_LVLMSK; + /* Added by mistake in early 2007. Probably useless, but it could + * be working around some unknown firmware problem where the value + * read at startup doesn't match the real hardware state... so leave + * it in place just in case */ backlight_update_status(ibm_backlight_device); vdbg_printk(TPACPI_DBG_INIT | TPACPI_DBG_BRGHT, @@ -6271,11 +6450,11 @@ static int __init brightness_init(struct ibm_init_struct *iibm) "as change notification\n"); tpacpi_hotkey_driver_mask_set(hotkey_driver_mask | TP_ACPI_HKEY_BRGHTUP_MASK - | TP_ACPI_HKEY_BRGHTDWN_MASK);; + | TP_ACPI_HKEY_BRGHTDWN_MASK); return 0; } -static void brightness_suspend(pm_message_t state) +static void brightness_suspend(void) { tpacpi_brightness_checkpoint_nvram(); } @@ -6306,9 +6485,8 @@ static int brightness_read(struct seq_file *m) } else { seq_printf(m, "level:\t\t%d\n", level); seq_printf(m, "commands:\tup, down\n"); - seq_printf(m, "commands:\tlevel <level>" - " (<level> is 0-%d)\n", - (tp_features.bright_16levels) ? 15 : 7); + seq_printf(m, "commands:\tlevel <level> (<level> is 0-%d)\n", + bright_maxlvl); } return 0; @@ -6319,7 +6497,6 @@ static int brightness_write(char *buf) int level; int rc; char *cmd; - int max_level = (tp_features.bright_16levels) ? 15 : 7; level = brightness_get(NULL); if (level < 0) @@ -6327,13 +6504,13 @@ static int brightness_write(char *buf) while ((cmd = next_cmd(&buf))) { if (strlencmp(cmd, "up") == 0) { - if (level < max_level) + if (level < bright_maxlvl) level++; } else if (strlencmp(cmd, "down") == 0) { if (level > 0) level--; } else if (sscanf(cmd, "level %d", &level) == 1 && - level >= 0 && level <= max_level) { + level >= 0 && level <= bright_maxlvl) { /* new level set */ } else return -EINVAL; @@ -6390,9 +6567,14 @@ static struct ibm_struct brightness_driver_data = { #define TPACPI_ALSA_SHRTNAME "ThinkPad Console Audio Control" #define TPACPI_ALSA_MIXERNAME TPACPI_ALSA_SHRTNAME -static int alsa_index = ~((1 << (SNDRV_CARDS - 3)) - 1); /* last three slots */ +#if SNDRV_CARDS <= 32 +#define DEFAULT_ALSA_IDX ~((1 << (SNDRV_CARDS - 3)) - 1) +#else +#define DEFAULT_ALSA_IDX ~((1 << (32 - 3)) - 1) +#endif +static int alsa_index = DEFAULT_ALSA_IDX; /* last three slots */ static char *alsa_id = "ThinkPadEC"; -static int alsa_enable = SNDRV_DEFAULT_ENABLE1; +static bool alsa_enable = SNDRV_DEFAULT_ENABLE1; struct tpacpi_alsa_data { struct snd_card *card; @@ -6435,7 +6617,7 @@ static enum tpacpi_volume_access_mode volume_mode = TPACPI_VOL_MODE_MAX; static enum tpacpi_volume_capabilities volume_capabilities; -static int volume_control_allowed; +static bool volume_control_allowed; /* * Used to syncronize writers to TP_EC_AUDIO and @@ -6522,7 +6704,8 @@ static int volume_set_status(const u8 status) return volume_set_status_ec(status); } -static int volume_set_mute_ec(const bool mute) +/* returns < 0 on error, 0 on no change, 1 on change */ +static int __volume_set_mute_ec(const bool mute) { int rc; u8 s, n; @@ -6537,22 +6720,37 @@ static int volume_set_mute_ec(const bool mute) n = (mute) ? s | TP_EC_AUDIO_MUTESW_MSK : s & ~TP_EC_AUDIO_MUTESW_MSK; - if (n != s) + if (n != s) { rc = volume_set_status_ec(n); + if (!rc) + rc = 1; + } unlock: mutex_unlock(&volume_mutex); return rc; } +static int volume_alsa_set_mute(const bool mute) +{ + dbg_printk(TPACPI_DBG_MIXER, "ALSA: trying to %smute\n", + (mute) ? "" : "un"); + return __volume_set_mute_ec(mute); +} + static int volume_set_mute(const bool mute) { + int rc; + dbg_printk(TPACPI_DBG_MIXER, "trying to %smute\n", (mute) ? "" : "un"); - return volume_set_mute_ec(mute); + + rc = __volume_set_mute_ec(mute); + return (rc < 0) ? rc : 0; } -static int volume_set_volume_ec(const u8 vol) +/* returns < 0 on error, 0 on no change, 1 on change */ +static int __volume_set_volume_ec(const u8 vol) { int rc; u8 s, n; @@ -6569,19 +6767,22 @@ static int volume_set_volume_ec(const u8 vol) n = (s & ~TP_EC_AUDIO_LVL_MSK) | vol; - if (n != s) + if (n != s) { rc = volume_set_status_ec(n); + if (!rc) + rc = 1; + } unlock: mutex_unlock(&volume_mutex); return rc; } -static int volume_set_volume(const u8 vol) +static int volume_alsa_set_volume(const u8 vol) { dbg_printk(TPACPI_DBG_MIXER, - "trying to set volume level to %hu\n", vol); - return volume_set_volume_ec(vol); + "ALSA: trying to set volume level to %hu\n", vol); + return __volume_set_volume_ec(vol); } static void volume_alsa_notify_change(void) @@ -6628,7 +6829,9 @@ static int volume_alsa_vol_get(struct snd_kcontrol *kcontrol, static int volume_alsa_vol_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - return volume_set_volume(ucontrol->value.integer.value[0]); + tpacpi_disclose_usertask("ALSA", "set volume to %ld\n", + ucontrol->value.integer.value[0]); + return volume_alsa_set_volume(ucontrol->value.integer.value[0]); } #define volume_alsa_mute_info snd_ctl_boolean_mono_info @@ -6651,10 +6854,13 @@ static int volume_alsa_mute_get(struct snd_kcontrol *kcontrol, static int volume_alsa_mute_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - return volume_set_mute(!ucontrol->value.integer.value[0]); + tpacpi_disclose_usertask("ALSA", "%smute\n", + ucontrol->value.integer.value[0] ? + "un" : ""); + return volume_alsa_set_mute(!ucontrol->value.integer.value[0]); } -static struct snd_kcontrol_new volume_alsa_control_vol __devinitdata = { +static struct snd_kcontrol_new volume_alsa_control_vol = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "Console Playback Volume", .index = 0, @@ -6663,7 +6869,7 @@ static struct snd_kcontrol_new volume_alsa_control_vol __devinitdata = { .get = volume_alsa_vol_get, }; -static struct snd_kcontrol_new volume_alsa_control_mute __devinitdata = { +static struct snd_kcontrol_new volume_alsa_control_mute = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "Console Playback Switch", .index = 0, @@ -6672,7 +6878,7 @@ static struct snd_kcontrol_new volume_alsa_control_mute __devinitdata = { .get = volume_alsa_mute_get, }; -static void volume_suspend(pm_message_t state) +static void volume_suspend(void) { tpacpi_volume_checkpoint_nvram(); } @@ -6705,11 +6911,11 @@ static int __init volume_create_alsa_mixer(void) struct snd_kcontrol *ctl_mute; int rc; - rc = snd_card_create(alsa_index, alsa_id, THIS_MODULE, - sizeof(struct tpacpi_alsa_data), &card); + rc = snd_card_new(&tpacpi_pdev->dev, + alsa_index, alsa_id, THIS_MODULE, + sizeof(struct tpacpi_alsa_data), &card); if (rc < 0 || !card) { - printk(TPACPI_ERR - "Failed to create ALSA card structures: %d\n", rc); + pr_err("Failed to create ALSA card structures: %d\n", rc); return 1; } @@ -6743,9 +6949,8 @@ static int __init volume_create_alsa_mixer(void) ctl_vol = snd_ctl_new1(&volume_alsa_control_vol, NULL); rc = snd_ctl_add(card, ctl_vol); if (rc < 0) { - printk(TPACPI_ERR - "Failed to create ALSA volume control: %d\n", - rc); + pr_err("Failed to create ALSA volume control: %d\n", + rc); goto err_exit; } data->ctl_vol_id = &ctl_vol->id; @@ -6754,16 +6959,14 @@ static int __init volume_create_alsa_mixer(void) ctl_mute = snd_ctl_new1(&volume_alsa_control_mute, NULL); rc = snd_ctl_add(card, ctl_mute); if (rc < 0) { - printk(TPACPI_ERR "Failed to create ALSA mute control: %d\n", - rc); + pr_err("Failed to create ALSA mute control: %d\n", rc); goto err_exit; } data->ctl_mute_id = &ctl_mute->id; - snd_card_set_dev(card, &tpacpi_pdev->dev); rc = snd_card_register(card); if (rc < 0) { - printk(TPACPI_ERR "Failed to register ALSA card: %d\n", rc); + pr_err("Failed to register ALSA card: %d\n", rc); goto err_exit; } @@ -6819,9 +7022,8 @@ static int __init volume_init(struct ibm_init_struct *iibm) return -EINVAL; if (volume_mode == TPACPI_VOL_MODE_UCMS_STEP) { - printk(TPACPI_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; } @@ -6885,13 +7087,11 @@ static int __init volume_init(struct ibm_init_struct *iibm) rc = volume_create_alsa_mixer(); if (rc) { - printk(TPACPI_ERR - "Could not create the ALSA mixer interface\n"); + pr_err("Could not create the ALSA mixer interface\n"); return rc; } - printk(TPACPI_INFO - "Console audio control enabled, mode: %s\n", + pr_info("Console audio control enabled, mode: %s\n", (volume_control_allowed) ? "override (read/write)" : "monitor (read only)"); @@ -6953,12 +7153,10 @@ 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; - printk(TPACPI_NOTICE - "Console audio control in monitor mode, " - "changes are not allowed.\n"); - printk(TPACPI_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; } @@ -7033,8 +7231,7 @@ static void inline volume_alsa_notify_change(void) static int __init volume_init(struct ibm_init_struct *iibm) { - printk(TPACPI_INFO - "volume: disabled as there is no ALSA support in this kernel\n"); + pr_info("volume: disabled as there is no ALSA support in this kernel\n"); return 1; } @@ -7070,7 +7267,7 @@ static struct ibm_struct volume_driver_data = { * * Fan speed changes of any sort (including those caused by the * disengaged mode) are usually done slowly by the firmware as the - * maximum ammount of fan duty cycle change per second seems to be + * maximum amount of fan duty cycle change per second seems to be * limited. * * Reading is not available if GFAN exists. @@ -7097,7 +7294,7 @@ static struct ibm_struct volume_driver_data = { * TPACPI_FAN_WR_ACPI_FANS (X31/X40/X41) * * FIRMWARE BUG: on some models, EC 0x2f might not be initialized at - * boot. Apparently the EC does not intialize it, so unless ACPI DSDT + * boot. Apparently the EC does not initialize it, so unless ACPI DSDT * does so, its initial value is meaningless (0x07). * * For firmware bugs, refer to: @@ -7198,7 +7395,7 @@ enum fan_control_commands { * and also watchdog cmd */ }; -static int fan_control_allowed; +static bool fan_control_allowed; static enum fan_status_access_mode fan_status_access_mode; static enum fan_control_access_mode fan_control_access_mode; @@ -7241,9 +7438,8 @@ TPACPI_HANDLE(sfan, ec, "SFAN", /* 570 */ static void fan_quirk1_setup(void) { if (fan_control_initial_status == 0x07) { - printk(TPACPI_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; } } @@ -7318,17 +7514,18 @@ static int fan_get_status(u8 *status) * Add TPACPI_FAN_RD_ACPI_FANS ? */ switch (fan_status_access_mode) { - case TPACPI_FAN_RD_ACPI_GFAN: + case TPACPI_FAN_RD_ACPI_GFAN: { /* 570, 600e/x, 770e, 770x */ + int res; - if (unlikely(!acpi_evalf(gfan_handle, &s, NULL, "d"))) + if (unlikely(!acpi_evalf(gfan_handle, &res, NULL, "d"))) return -EIO; if (likely(status)) - *status = s & 0x07; + *status = res & 0x07; break; - + } case TPACPI_FAN_RD_TPEC: /* all except 570, 600e/x, 770e, 770x */ if (unlikely(!acpi_ec_read(fan_status_offset, &s))) @@ -7616,26 +7813,15 @@ static int fan_set_speed(int speed) static void fan_watchdog_reset(void) { - static int fan_watchdog_active; - if (fan_control_access_mode == TPACPI_FAN_WR_NONE) return; - if (fan_watchdog_active) - cancel_delayed_work(&fan_watchdog_task); - if (fan_watchdog_maxinterval > 0 && - tpacpi_lifecycle != TPACPI_LIFE_EXITING) { - fan_watchdog_active = 1; - if (!queue_delayed_work(tpacpi_wq, &fan_watchdog_task, - msecs_to_jiffies(fan_watchdog_maxinterval - * 1000))) { - printk(TPACPI_ERR - "failed to queue the fan watchdog, " - "watchdog will not trigger\n"); - } - } else - fan_watchdog_active = 0; + tpacpi_lifecycle != TPACPI_LIFE_EXITING) + mod_delayed_work(tpacpi_wq, &fan_watchdog_task, + msecs_to_jiffies(fan_watchdog_maxinterval * 1000)); + else + cancel_delayed_work(&fan_watchdog_task); } static void fan_watchdog_fire(struct work_struct *ignored) @@ -7645,11 +7831,11 @@ static void fan_watchdog_fire(struct work_struct *ignored) if (tpacpi_lifecycle != TPACPI_LIFE_RUNNING) return; - printk(TPACPI_NOTICE "fan watchdog: enabling fan\n"); + pr_notice("fan watchdog: enabling fan\n"); rc = fan_set_enable(); if (rc < 0) { - printk(TPACPI_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(); } @@ -7927,9 +8113,11 @@ static int __init fan_init(struct ibm_init_struct *iibm) tp_features.second_fan = 0; fan_control_desired_level = 7; - TPACPI_ACPIHANDLE_INIT(fans); - TPACPI_ACPIHANDLE_INIT(gfan); - TPACPI_ACPIHANDLE_INIT(sfan); + if (tpacpi_is_ibm()) { + TPACPI_ACPIHANDLE_INIT(fans); + TPACPI_ACPIHANDLE_INIT(gfan); + TPACPI_ACPIHANDLE_INIT(sfan); + } quirks = tpacpi_check_quirks(fan_quirk_table, ARRAY_SIZE(fan_quirk_table)); @@ -7951,8 +8139,7 @@ static int __init fan_init(struct ibm_init_struct *iibm) "secondary fan support enabled\n"); } } else { - printk(TPACPI_ERR - "ThinkPad ACPI EC access misbehaving, " + pr_err("ThinkPad ACPI EC access misbehaving, " "fan status and control unavailable\n"); return 1; } @@ -8041,7 +8228,7 @@ static void fan_exit(void) flush_workqueue(tpacpi_wq); } -static void fan_suspend(pm_message_t state) +static void fan_suspend(void) { int rc; @@ -8052,9 +8239,8 @@ static void fan_suspend(pm_message_t state) fan_control_resume_level = 0; rc = fan_get_status_safe(&fan_control_resume_level); if (rc < 0) - printk(TPACPI_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 */ @@ -8109,13 +8295,11 @@ static void fan_resume(void) return; } if (do_set) { - printk(TPACPI_NOTICE - "restoring fan level to 0x%02x\n", - fan_control_resume_level); + pr_notice("restoring fan level to 0x%02x\n", + fan_control_resume_level); rc = fan_set_level_safe(fan_control_resume_level); if (rc < 0) - printk(TPACPI_NOTICE - "failed to restore fan level: %d\n", rc); + pr_notice("failed to restore fan level: %d\n", rc); } } @@ -8207,8 +8391,8 @@ static int fan_write_cmd_level(const char *cmd, int *rc) *rc = fan_set_level_safe(level); if (*rc == -ENXIO) - printk(TPACPI_ERR "level command accepted for unsupported " - "access mode %d", fan_control_access_mode); + pr_err("level command accepted for unsupported access mode %d\n", + fan_control_access_mode); else if (!*rc) tpacpi_disclose_usertask("procfs fan", "set level to %d\n", level); @@ -8223,8 +8407,8 @@ static int fan_write_cmd_enable(const char *cmd, int *rc) *rc = fan_set_enable(); if (*rc == -ENXIO) - printk(TPACPI_ERR "enable command accepted for unsupported " - "access mode %d", fan_control_access_mode); + pr_err("enable command accepted for unsupported access mode %d\n", + fan_control_access_mode); else if (!*rc) tpacpi_disclose_usertask("procfs fan", "enable\n"); @@ -8238,8 +8422,8 @@ static int fan_write_cmd_disable(const char *cmd, int *rc) *rc = fan_set_disable(); if (*rc == -ENXIO) - printk(TPACPI_ERR "disable command accepted for unsupported " - "access mode %d", fan_control_access_mode); + pr_err("disable command accepted for unsupported access mode %d\n", + fan_control_access_mode); else if (!*rc) tpacpi_disclose_usertask("procfs fan", "disable\n"); @@ -8258,8 +8442,8 @@ static int fan_write_cmd_speed(const char *cmd, int *rc) *rc = fan_set_speed(speed); if (*rc == -ENXIO) - printk(TPACPI_ERR "speed command accepted for unsupported " - "access mode %d", fan_control_access_mode); + pr_err("speed command accepted for unsupported access mode %d\n", + fan_control_access_mode); else if (!*rc) tpacpi_disclose_usertask("procfs fan", "set speed to %d\n", speed); @@ -8318,6 +8502,103 @@ static struct ibm_struct fan_driver_data = { .resume = fan_resume, }; +/************************************************************************* + * Mute LED subdriver + */ + + +struct tp_led_table { + acpi_string name; + int on_value; + int off_value; + int state; +}; + +static struct tp_led_table led_tables[] = { + [TPACPI_LED_MUTE] = { + .name = "SSMS", + .on_value = 1, + .off_value = 0, + }, + [TPACPI_LED_MICMUTE] = { + .name = "MMTS", + .on_value = 2, + .off_value = 0, + }, +}; + +static int mute_led_on_off(struct tp_led_table *t, bool state) +{ + acpi_handle temp; + int output; + + if (!ACPI_SUCCESS(acpi_get_handle(hkey_handle, t->name, &temp))) { + pr_warn("Thinkpad ACPI has no %s interface.\n", t->name); + return -EIO; + } + + if (!acpi_evalf(hkey_handle, &output, t->name, "dd", + state ? t->on_value : t->off_value)) + return -EIO; + + t->state = state; + return state; +} + +int tpacpi_led_set(int whichled, bool on) +{ + struct tp_led_table *t; + + if (whichled < 0 || whichled >= TPACPI_LED_MAX) + return -EINVAL; + + t = &led_tables[whichled]; + if (t->state < 0 || t->state == on) + return t->state; + return mute_led_on_off(t, on); +} +EXPORT_SYMBOL_GPL(tpacpi_led_set); + +static int mute_led_init(struct ibm_init_struct *iibm) +{ + acpi_handle temp; + int i; + + for (i = 0; i < TPACPI_LED_MAX; i++) { + struct tp_led_table *t = &led_tables[i]; + if (ACPI_SUCCESS(acpi_get_handle(hkey_handle, t->name, &temp))) + mute_led_on_off(t, false); + else + t->state = -ENODEV; + } + return 0; +} + +static void mute_led_exit(void) +{ + int i; + + for (i = 0; i < TPACPI_LED_MAX; i++) + tpacpi_led_set(i, false); +} + +static void mute_led_resume(void) +{ + int i; + + for (i = 0; i < TPACPI_LED_MAX; i++) { + struct tp_led_table *t = &led_tables[i]; + if (t->state >= 0) + mute_led_on_off(t, t->state); + } +} + +static struct ibm_struct mute_led_driver_data = { + .name = "mute_led", + .exit = mute_led_exit, + .resume = mute_led_resume, +}; + /**************************************************************************** **************************************************************************** * @@ -8374,7 +8655,7 @@ static struct proc_dir_entry *proc_dir; * Module and infrastructure proble, init and exit handling */ -static int force_load; +static bool force_load; #ifdef CONFIG_THINKPAD_ACPI_DEBUG static const char * __init str_supported(int is_supported) @@ -8399,7 +8680,6 @@ static void ibm_exit(struct ibm_struct *ibm) ibm->acpi->type, dispatch_acpi_notify); ibm->flags.acpi_notify_installed = 0; - ibm->flags.acpi_notify_installed = 0; } if (ibm->flags.proc_created) { @@ -8463,8 +8743,8 @@ static int __init ibm_init(struct ibm_init_struct *iibm) if (ibm->acpi->notify) { ret = setup_acpi_notify(ibm); if (ret == -ENODEV) { - printk(TPACPI_NOTICE "disabling subdriver %s\n", - ibm->name); + pr_notice("disabling subdriver %s\n", + ibm->name); ret = 0; goto err_out; } @@ -8477,16 +8757,16 @@ static int __init ibm_init(struct ibm_init_struct *iibm) "%s installed\n", ibm->name); if (ibm->read) { - mode_t mode; + umode_t mode = iibm->base_procfs_mode; - mode = S_IRUGO; + if (!mode) + mode = S_IRUGO; if (ibm->write) mode |= S_IWUSR; entry = proc_create_data(ibm->name, mode, proc_dir, &dispatch_proc_fops, ibm); if (!entry) { - printk(TPACPI_ERR "unable to create proc entry %s\n", - ibm->name); + pr_err("unable to create proc entry %s\n", ibm->name); ret = -ENODEV; goto err_out; } @@ -8520,10 +8800,10 @@ static bool __pure __init tpacpi_is_valid_fw_id(const char* const s, return s && strlen(s) >= 8 && tpacpi_is_fw_digit(s[0]) && tpacpi_is_fw_digit(s[1]) && - s[2] == t && s[3] == 'T' && + s[2] == t && + (s[3] == 'T' || s[3] == 'N') && tpacpi_is_fw_digit(s[4]) && - tpacpi_is_fw_digit(s[5]) && - s[6] == 'W' && s[7] == 'W'; + tpacpi_is_fw_digit(s[5]); } /* returns 0 - probe ok, or < 0 - probe error. @@ -8554,7 +8834,8 @@ static int __must_check __init get_thinkpad_model_data( return -ENOMEM; /* Really ancient ThinkPad 240X will fail this, which is fine */ - if (!tpacpi_is_valid_fw_id(tp->bios_version_str, 'E')) + if (!(tpacpi_is_valid_fw_id(tp->bios_version_str, 'E') || + tpacpi_is_valid_fw_id(tp->bios_version_str, 'C'))) return 0; tp->bios_model = tp->bios_version_str[0] @@ -8586,23 +8867,28 @@ static int __must_check __init get_thinkpad_model_data( tp->ec_release = (ec_fw_string[4] << 8) | ec_fw_string[5]; } else { - printk(TPACPI_NOTICE - "ThinkPad firmware release %s " - "doesn't match the known patterns\n", - ec_fw_string); - printk(TPACPI_NOTICE - "please report this to %s\n", - TPACPI_MAIL); + 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); } break; } } s = dmi_get_system_info(DMI_PRODUCT_VERSION); - if (s && !strnicmp(s, "ThinkPad", 8)) { + if (s && !(strnicmp(s, "ThinkPad", 8) && strnicmp(s, "Lenovo", 6))) { tp->model_str = kstrdup(s, GFP_KERNEL); if (!tp->model_str) return -ENOMEM; + } else { + s = dmi_get_system_info(DMI_BIOS_VENDOR); + if (s && !(strnicmp(s, "Lenovo", 6))) { + tp->model_str = kstrdup(s, GFP_KERNEL); + if (!tp->model_str) + return -ENOMEM; + } } s = dmi_get_system_info(DMI_PRODUCT_NAME); @@ -8620,6 +8906,10 @@ static int __init probe_for_thinkpad(void) if (acpi_disabled) return -ENODEV; + /* It would be dangerous to run the driver in this case */ + if (!tpacpi_is_ibm() && !tpacpi_is_lenovo()) + return -ENODEV; + /* * Non-ancient models have better DMI tagging, but very old models * don't. tpacpi_is_fw_known() is a cheat to help in that case. @@ -8628,12 +8918,11 @@ static int __init probe_for_thinkpad(void) (thinkpad_id.ec_model != 0) || tpacpi_is_fw_known(); - /* ec is required because many other handles are relative to it */ - TPACPI_ACPIHANDLE_INIT(ec); + /* The EC handler is required */ + tpacpi_acpi_handle_locate("ec", TPACPI_ACPI_EC_HID, &ec_handle); if (!ec_handle) { if (is_thinkpad) - printk(TPACPI_ERR - "Not yet supported ThinkPad detected!\n"); + pr_err("Not yet supported ThinkPad detected!\n"); return -ENODEV; } @@ -8643,12 +8932,34 @@ static int __init probe_for_thinkpad(void) return 0; } +static void __init thinkpad_acpi_init_banner(void) +{ + pr_info("%s v%s\n", TPACPI_DESC, TPACPI_VERSION); + pr_info("%s\n", TPACPI_URL); + + pr_info("ThinkPad BIOS %s, EC %s\n", + (thinkpad_id.bios_version_str) ? + thinkpad_id.bios_version_str : "unknown", + (thinkpad_id.ec_version_str) ? + thinkpad_id.ec_version_str : "unknown"); + + BUG_ON(!thinkpad_id.vendor); + + if (thinkpad_id.model_str) + pr_info("%s %s, model %s\n", + (thinkpad_id.vendor == PCI_VENDOR_ID_IBM) ? + "IBM" : ((thinkpad_id.vendor == + PCI_VENDOR_ID_LENOVO) ? + "Lenovo" : "Unknown vendor"), + thinkpad_id.model_str, + (thinkpad_id.nummodel_str) ? + thinkpad_id.nummodel_str : "unknown"); +} /* Module init, exit, parameters */ static struct ibm_init_struct ibms_init[] __initdata = { { - .init = thinkpad_acpi_driver_init, .data = &thinkpad_acpi_driver_data, }, { @@ -8670,6 +8981,7 @@ static struct ibm_init_struct ibms_init[] __initdata = { #ifdef CONFIG_THINKPAD_ACPI_VIDEO { .init = video_init, + .base_procfs_mode = S_IRUSR, .data = &video_driver_data, }, #endif @@ -8694,9 +9006,6 @@ static struct ibm_init_struct ibms_init[] __initdata = { .data = &thermal_driver_data, }, { - .data = &ecdump_driver_data, - }, - { .init = brightness_init, .data = &brightness_driver_data, }, @@ -8708,6 +9017,10 @@ static struct ibm_init_struct ibms_init[] __initdata = { .init = fan_init, .data = &fan_driver_data, }, + { + .init = mute_led_init, + .data = &mute_led_driver_data, + }, }; static int __init set_ibm_param(const char *val, struct kernel_param *kp) @@ -8762,11 +9075,6 @@ module_param(brightness_enable, uint, 0444); MODULE_PARM_DESC(brightness_enable, "Enables backlight control when 1, disables when 0"); -module_param(hotkey_report_mode, uint, 0444); -MODULE_PARM_DESC(hotkey_report_mode, - "used for backwards compatibility with userspace, " - "see documentation"); - #ifdef CONFIG_THINKPAD_ACPI_ALSA_SUPPORT module_param_named(volume_mode, volume_mode, uint, 0444); MODULE_PARM_DESC(volume_mode, @@ -8804,7 +9112,6 @@ TPACPI_PARAM(light); TPACPI_PARAM(cmos); TPACPI_PARAM(led); TPACPI_PARAM(beep); -TPACPI_PARAM(ecdump); TPACPI_PARAM(brightness); TPACPI_PARAM(volume); TPACPI_PARAM(fan); @@ -8854,6 +9161,7 @@ static void thinkpad_acpi_module_exit(void) input_unregister_device(tpacpi_inputdev); else input_free_device(tpacpi_inputdev); + kfree(hotkey_keycode_map); } if (tpacpi_hwmon) @@ -8887,6 +9195,7 @@ static void thinkpad_acpi_module_exit(void) kfree(thinkpad_id.bios_version_str); kfree(thinkpad_id.ec_version_str); kfree(thinkpad_id.model_str); + kfree(thinkpad_id.nummodel_str); } @@ -8896,16 +9205,11 @@ static int __init thinkpad_acpi_module_init(void) tpacpi_lifecycle = TPACPI_LIFE_INIT; - /* Parameter checking */ - if (hotkey_report_mode > 2) - return -EINVAL; - /* Driver-level probe */ ret = get_thinkpad_model_data(&thinkpad_id); if (ret) { - printk(TPACPI_ERR - "unable to get DMI data: %d\n", ret); + pr_err("unable to get DMI data: %d\n", ret); thinkpad_acpi_module_exit(); return ret; } @@ -8917,6 +9221,9 @@ static int __init thinkpad_acpi_module_init(void) /* Driver initialization */ + thinkpad_acpi_init_banner(); + tpacpi_check_outdated_fw(); + TPACPI_ACPIHANDLE_INIT(ecrd); TPACPI_ACPIHANDLE_INIT(ecwr); @@ -8928,16 +9235,14 @@ static int __init thinkpad_acpi_module_init(void) proc_dir = proc_mkdir(TPACPI_PROC_DIR, acpi_root_dir); if (!proc_dir) { - printk(TPACPI_ERR - "unable to create proc dir " TPACPI_PROC_DIR); + pr_err("unable to create proc dir " TPACPI_PROC_DIR "\n"); thinkpad_acpi_module_exit(); return -ENODEV; } ret = platform_driver_register(&tpacpi_pdriver); if (ret) { - printk(TPACPI_ERR - "unable to register main platform driver\n"); + pr_err("unable to register main platform driver\n"); thinkpad_acpi_module_exit(); return ret; } @@ -8945,8 +9250,7 @@ static int __init thinkpad_acpi_module_init(void) ret = platform_driver_register(&tpacpi_hwmon_pdriver); if (ret) { - printk(TPACPI_ERR - "unable to register hwmon platform driver\n"); + pr_err("unable to register hwmon platform driver\n"); thinkpad_acpi_module_exit(); return ret; } @@ -8959,8 +9263,7 @@ static int __init thinkpad_acpi_module_init(void) &tpacpi_hwmon_pdriver.driver); } if (ret) { - printk(TPACPI_ERR - "unable to create sysfs driver attributes\n"); + pr_err("unable to create sysfs driver attributes\n"); thinkpad_acpi_module_exit(); return ret; } @@ -8973,7 +9276,7 @@ static int __init thinkpad_acpi_module_init(void) if (IS_ERR(tpacpi_pdev)) { ret = PTR_ERR(tpacpi_pdev); tpacpi_pdev = NULL; - printk(TPACPI_ERR "unable to register platform device\n"); + pr_err("unable to register platform device\n"); thinkpad_acpi_module_exit(); return ret; } @@ -8983,16 +9286,14 @@ static int __init thinkpad_acpi_module_init(void) if (IS_ERR(tpacpi_sensors_pdev)) { ret = PTR_ERR(tpacpi_sensors_pdev); tpacpi_sensors_pdev = NULL; - printk(TPACPI_ERR - "unable to register hwmon platform device\n"); + pr_err("unable to register hwmon platform device\n"); thinkpad_acpi_module_exit(); return ret; } ret = device_create_file(&tpacpi_sensors_pdev->dev, &dev_attr_thinkpad_acpi_pdev_name); if (ret) { - printk(TPACPI_ERR - "unable to create sysfs hwmon device attributes\n"); + pr_err("unable to create sysfs hwmon device attributes\n"); thinkpad_acpi_module_exit(); return ret; } @@ -9001,14 +9302,13 @@ static int __init thinkpad_acpi_module_init(void) if (IS_ERR(tpacpi_hwmon)) { ret = PTR_ERR(tpacpi_hwmon); tpacpi_hwmon = NULL; - printk(TPACPI_ERR "unable to register hwmon device\n"); + pr_err("unable to register hwmon device\n"); thinkpad_acpi_module_exit(); return ret; } mutex_init(&tpacpi_inputdev_send_mutex); tpacpi_inputdev = input_allocate_device(); if (!tpacpi_inputdev) { - printk(TPACPI_ERR "unable to allocate input device\n"); thinkpad_acpi_module_exit(); return -ENOMEM; } else { @@ -9016,13 +9316,16 @@ static int __init thinkpad_acpi_module_init(void) tpacpi_inputdev->name = "ThinkPad Extra Buttons"; tpacpi_inputdev->phys = TPACPI_DRVR_NAME "/input0"; tpacpi_inputdev->id.bustype = BUS_HOST; - tpacpi_inputdev->id.vendor = (thinkpad_id.vendor) ? - thinkpad_id.vendor : - PCI_VENDOR_ID_IBM; + tpacpi_inputdev->id.vendor = thinkpad_id.vendor; tpacpi_inputdev->id.product = TPACPI_HKEY_INPUT_PRODUCT; tpacpi_inputdev->id.version = TPACPI_HKEY_INPUT_VERSION; tpacpi_inputdev->dev.parent = &tpacpi_pdev->dev; } + + /* Init subdriver dependencies */ + tpacpi_detect_brightness_capabilities(); + + /* Init subdrivers */ for (i = 0; i < ARRAY_SIZE(ibms_init); i++) { ret = ibm_init(&ibms_init[i]); if (ret >= 0 && *ibms_init[i].param) @@ -9032,16 +9335,18 @@ static int __init thinkpad_acpi_module_init(void) return ret; } } + + tpacpi_lifecycle = TPACPI_LIFE_RUNNING; + ret = input_register_device(tpacpi_inputdev); if (ret < 0) { - printk(TPACPI_ERR "unable to register input device\n"); + pr_err("unable to register input device\n"); thinkpad_acpi_module_exit(); return ret; } else { tp_features.input_device_registered = 1; } - tpacpi_lifecycle = TPACPI_LIFE_RUNNING; return 0; } |
