diff options
Diffstat (limited to 'drivers/platform/x86/thinkpad_acpi.c')
-rw-r--r-- | drivers/platform/x86/thinkpad_acpi.c | 1392 |
1 files changed, 732 insertions, 660 deletions
diff --git a/drivers/platform/x86/thinkpad_acpi.c b/drivers/platform/x86/thinkpad_acpi.c index 82429e59999d..e7350c9fa3aa 100644 --- a/drivers/platform/x86/thinkpad_acpi.c +++ b/drivers/platform/x86/thinkpad_acpi.c @@ -39,12 +39,12 @@ #include <linux/bitops.h> #include <linux/delay.h> #include <linux/dmi.h> -#include <linux/fb.h> #include <linux/freezer.h> #include <linux/hwmon.h> #include <linux/hwmon-sysfs.h> #include <linux/init.h> #include <linux/input.h> +#include <linux/input/sparse-keymap.h> #include <linux/jiffies.h> #include <linux/kernel.h> #include <linux/kthread.h> @@ -157,17 +157,35 @@ enum { /* HKEY events */ enum tpacpi_hkey_event_t { - /* Hotkey-related */ - TP_HKEY_EV_HOTKEY_BASE = 0x1001, /* first hotkey (FN+F1) */ + /* Original hotkeys */ + TP_HKEY_EV_ORIG_KEY_START = 0x1001, /* First hotkey (FN+F1) */ TP_HKEY_EV_BRGHT_UP = 0x1010, /* Brightness up */ TP_HKEY_EV_BRGHT_DOWN = 0x1011, /* Brightness down */ TP_HKEY_EV_KBD_LIGHT = 0x1012, /* Thinklight/kbd backlight */ TP_HKEY_EV_VOL_UP = 0x1015, /* Volume up or unmute */ TP_HKEY_EV_VOL_DOWN = 0x1016, /* Volume down or unmute */ TP_HKEY_EV_VOL_MUTE = 0x1017, /* Mixer output mute */ + TP_HKEY_EV_ORIG_KEY_END = 0x1020, /* Last original hotkey code */ + + /* Adaptive keyboard (2014 X1 Carbon) */ + TP_HKEY_EV_DFR_CHANGE_ROW = 0x1101, /* Change adaptive kbd Fn row mode */ + TP_HKEY_EV_DFR_S_QUICKVIEW_ROW = 0x1102, /* Set adap. kbd Fn row to function mode */ + TP_HKEY_EV_ADAPTIVE_KEY_START = 0x1103, /* First hotkey code on adaptive kbd */ + TP_HKEY_EV_ADAPTIVE_KEY_END = 0x1116, /* Last hotkey code on adaptive kbd */ + + /* Extended hotkey events in 2017+ models */ + TP_HKEY_EV_EXTENDED_KEY_START = 0x1300, /* First extended hotkey code */ TP_HKEY_EV_PRIVACYGUARD_TOGGLE = 0x130f, /* Toggle priv.guard on/off */ + TP_HKEY_EV_EXTENDED_KEY_END = 0x1319, /* Last extended hotkey code using + * hkey -> scancode translation for + * compat. Later codes are entered + * directly in the sparse-keymap. + */ TP_HKEY_EV_AMT_TOGGLE = 0x131a, /* Toggle AMT on/off */ - TP_HKEY_EV_PROFILE_TOGGLE = 0x131f, /* Toggle platform profile */ + TP_HKEY_EV_CAMERASHUTTER_TOGGLE = 0x131b, /* Toggle Camera Shutter */ + TP_HKEY_EV_DOUBLETAP_TOGGLE = 0x131c, /* Toggle trackpoint doubletap on/off */ + TP_HKEY_EV_PROFILE_TOGGLE = 0x131f, /* Toggle platform profile in 2024 systems */ + TP_HKEY_EV_PROFILE_TOGGLE2 = 0x1401, /* Toggle platform profile in 2025 + systems */ /* Reasons for waking up from S3/S4 */ TP_HKEY_EV_WKUP_S3_UNDOCK = 0x2304, /* undock requested, S3 */ @@ -214,6 +232,7 @@ enum tpacpi_hkey_event_t { /* Thermal events */ TP_HKEY_EV_ALARM_BAT_HOT = 0x6011, /* battery too hot */ TP_HKEY_EV_ALARM_BAT_XHOT = 0x6012, /* battery critically hot */ + TP_HKEY_EV_ALARM_BAT_LIM_CHANGE = 0x6013, /* battery charge limit changed*/ TP_HKEY_EV_ALARM_SENSOR_HOT = 0x6021, /* sensor too hot */ TP_HKEY_EV_ALARM_SENSOR_XHOT = 0x6022, /* sensor critically hot */ TP_HKEY_EV_THM_TABLE_CHANGED = 0x6030, /* windows; thermal table changed */ @@ -232,6 +251,9 @@ enum tpacpi_hkey_event_t { /* Misc */ TP_HKEY_EV_RFKILL_CHANGED = 0x7000, /* rfkill switch changed */ + + /* Misc2 */ + TP_HKEY_EV_TRACK_DOUBLETAP = 0x8036, /* trackpoint doubletap */ }; /**************************************************************************** @@ -347,12 +369,11 @@ static struct { u32 beep_needs_two_args:1; u32 mixer_no_level_control:1; u32 battery_force_primary:1; - u32 input_device_registered:1; u32 platform_drv_registered:1; - u32 sensors_pdrv_registered:1; u32 hotkey_poll_active:1; u32 has_adaptive_kbd:1; u32 kbd_lang:1; + u32 trackpoint_doubletap:1; struct quirk_entry *quirks; } tp_features; @@ -817,9 +838,9 @@ static int __init setup_acpi_notify(struct ibm_struct *ibm) } ibm->acpi->device->driver_data = ibm; - sprintf(acpi_device_class(ibm->acpi->device), "%s/%s", - TPACPI_ACPI_EVENT_PREFIX, - ibm->name); + scnprintf(acpi_device_class(ibm->acpi->device), + sizeof(acpi_device_class(ibm->acpi->device)), + "%s/%s", TPACPI_ACPI_EVENT_PREFIX, ibm->name); status = acpi_install_notify_handler(*ibm->acpi->handle, ibm->acpi->type, dispatch_acpi_notify, ibm); @@ -941,6 +962,7 @@ static const struct proc_ops dispatch_proc_ops = { static struct platform_device *tpacpi_pdev; static struct platform_device *tpacpi_sensors_pdev; static struct device *tpacpi_hwmon; +static struct device *tpacpi_pprof; static struct input_dev *tpacpi_inputdev; static struct mutex tpacpi_inputdev_send_mutex; static LIST_HEAD(tpacpi_all_drivers); @@ -1744,15 +1766,15 @@ enum { /* hot key scan codes (derived from ACPI DSDT) */ TP_ACPI_HOTKEYSCAN_THINKPAD, TP_ACPI_HOTKEYSCAN_UNK1, TP_ACPI_HOTKEYSCAN_UNK2, - TP_ACPI_HOTKEYSCAN_UNK3, + TP_ACPI_HOTKEYSCAN_MICMUTE, TP_ACPI_HOTKEYSCAN_UNK4, - TP_ACPI_HOTKEYSCAN_UNK5, - TP_ACPI_HOTKEYSCAN_UNK6, - TP_ACPI_HOTKEYSCAN_UNK7, - TP_ACPI_HOTKEYSCAN_UNK8, + TP_ACPI_HOTKEYSCAN_CONFIG, + TP_ACPI_HOTKEYSCAN_SEARCH, + TP_ACPI_HOTKEYSCAN_SCALE, + TP_ACPI_HOTKEYSCAN_FILE, /* Adaptive keyboard keycodes */ - TP_ACPI_HOTKEYSCAN_ADAPTIVE_START, + TP_ACPI_HOTKEYSCAN_ADAPTIVE_START, /* 32 / 0x20 */ TP_ACPI_HOTKEYSCAN_MUTE2 = TP_ACPI_HOTKEYSCAN_ADAPTIVE_START, TP_ACPI_HOTKEYSCAN_BRIGHTNESS_ZERO, TP_ACPI_HOTKEYSCAN_CLIPPING_TOOL, @@ -1764,7 +1786,7 @@ enum { /* hot key scan codes (derived from ACPI DSDT) */ TP_ACPI_HOTKEYSCAN_UNK11, TP_ACPI_HOTKEYSCAN_UNK12, TP_ACPI_HOTKEYSCAN_UNK13, - TP_ACPI_HOTKEYSCAN_CONFIG, + TP_ACPI_HOTKEYSCAN_CONFIG2, TP_ACPI_HOTKEYSCAN_NEW_TAB, TP_ACPI_HOTKEYSCAN_RELOAD, TP_ACPI_HOTKEYSCAN_BACK, @@ -1775,7 +1797,7 @@ enum { /* hot key scan codes (derived from ACPI DSDT) */ TP_ACPI_HOTKEYSCAN_ROTATE_DISPLAY, /* Lenovo extended keymap, starting at 0x1300 */ - TP_ACPI_HOTKEYSCAN_EXTENDED_START, + TP_ACPI_HOTKEYSCAN_EXTENDED_START, /* 52 / 0x34 */ /* first new observed key (star, favorites) is 0x1311 */ TP_ACPI_HOTKEYSCAN_STAR = 69, TP_ACPI_HOTKEYSCAN_CLIPPING_TOOL2, @@ -1786,9 +1808,6 @@ enum { /* hot key scan codes (derived from ACPI DSDT) */ TP_ACPI_HOTKEYSCAN_NOTIFICATION_CENTER, TP_ACPI_HOTKEYSCAN_PICKUP_PHONE, TP_ACPI_HOTKEYSCAN_HANGUP_PHONE, - - /* Hotkey keymap size */ - TPACPI_HOTKEY_MAP_LEN }; enum { /* Keys/events available through NVRAM polling */ @@ -1901,10 +1920,7 @@ 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 u16 *hotkey_keycode_map; - -static void tpacpi_driver_event(const unsigned int hkey_event); -static void hotkey_driver_event(const unsigned int scancode); +static bool tpacpi_driver_event(const unsigned int hkey_event); static void hotkey_poll_setup(const bool may_warn); /* HKEY.MHKG() return bits */ @@ -2236,32 +2252,75 @@ static void tpacpi_input_send_tabletsw(void) } } -/* Do NOT call without validating scancode first */ -static void tpacpi_input_send_key(const unsigned int scancode) +#define GCES_NO_SHUTTER_DEVICE BIT(31) + +static int get_camera_shutter(void) { - const unsigned int keycode = hotkey_keycode_map[scancode]; + acpi_handle gces_handle; + int output; - if (keycode != KEY_RESERVED) { - mutex_lock(&tpacpi_inputdev_send_mutex); + if (ACPI_FAILURE(acpi_get_handle(hkey_handle, "GCES", &gces_handle))) + return -ENODEV; - input_event(tpacpi_inputdev, EV_MSC, MSC_SCAN, scancode); - input_report_key(tpacpi_inputdev, keycode, 1); - input_sync(tpacpi_inputdev); + if (!acpi_evalf(gces_handle, &output, NULL, "dd", 0)) + return -EIO; - input_event(tpacpi_inputdev, EV_MSC, MSC_SCAN, scancode); - input_report_key(tpacpi_inputdev, keycode, 0); - input_sync(tpacpi_inputdev); + if (output & GCES_NO_SHUTTER_DEVICE) + return -ENODEV; - mutex_unlock(&tpacpi_inputdev_send_mutex); - } + return output; } -/* Do NOT call without validating scancode first */ -static void tpacpi_input_send_key_masked(const unsigned int scancode) +static bool tpacpi_input_send_key(const u32 hkey, bool *send_acpi_ev) { - hotkey_driver_event(scancode); - if (hotkey_user_mask & (1 << scancode)) - tpacpi_input_send_key(scancode); + bool known_ev; + u32 scancode; + + if (tpacpi_driver_event(hkey)) + return true; + + /* + * Before the conversion to using the sparse-keymap helpers the driver used to + * map the hkey event codes to 0x00 - 0x4d scancodes so that a straight scancode + * indexed array could be used to map scancodes to keycodes: + * + * 0x1001 - 0x1020 -> 0x00 - 0x1f (Original ThinkPad events) + * 0x1103 - 0x1116 -> 0x20 - 0x33 (Adaptive keyboard, 2014 X1 Carbon) + * 0x1300 - 0x1319 -> 0x34 - 0x4d (Additional keys send in 2017+ models) + * + * The sparse-keymap tables still use these scancodes for these ranges to + * preserve userspace API compatibility (e.g. hwdb keymappings). + */ + if (hkey >= TP_HKEY_EV_ORIG_KEY_START && + hkey <= TP_HKEY_EV_ORIG_KEY_END) { + scancode = hkey - TP_HKEY_EV_ORIG_KEY_START; + if (!(hotkey_user_mask & (1 << scancode))) + return true; /* Not reported but still a known code */ + } else if (hkey >= TP_HKEY_EV_ADAPTIVE_KEY_START && + hkey <= TP_HKEY_EV_ADAPTIVE_KEY_END) { + scancode = hkey - TP_HKEY_EV_ADAPTIVE_KEY_START + + TP_ACPI_HOTKEYSCAN_ADAPTIVE_START; + } else if (hkey >= TP_HKEY_EV_EXTENDED_KEY_START && + hkey <= TP_HKEY_EV_EXTENDED_KEY_END) { + scancode = hkey - TP_HKEY_EV_EXTENDED_KEY_START + + TP_ACPI_HOTKEYSCAN_EXTENDED_START; + } else { + /* + * Do not send ACPI netlink events for unknown hotkeys, to + * avoid userspace starting to rely on them. Instead these + * should be added to the keymap to send evdev events. + */ + if (send_acpi_ev) + *send_acpi_ev = false; + + scancode = hkey; + } + + mutex_lock(&tpacpi_inputdev_send_mutex); + known_ev = sparse_keymap_report_event(tpacpi_inputdev, scancode, 1, true); + mutex_unlock(&tpacpi_inputdev_send_mutex); + + return known_ev; } #ifdef CONFIG_THINKPAD_ACPI_HOTKEY_POLL @@ -2270,7 +2329,7 @@ static struct tp_acpi_drv_struct ibm_hotkey_acpidriver; /* Do NOT call without validating scancode first */ static void tpacpi_hotkey_send_key(unsigned int scancode) { - tpacpi_input_send_key_masked(scancode); + tpacpi_input_send_key(TP_HKEY_EV_ORIG_KEY_START + scancode, NULL); } static void hotkey_read_nvram(struct tp_nvram_state *n, const u32 m) @@ -2575,6 +2634,9 @@ static void hotkey_poll_setup_safe(const bool __unused) { } +static void hotkey_poll_stop_sync(void) +{ +} #endif /* CONFIG_THINKPAD_ACPI_HOTKEY_POLL */ static int hotkey_inputdev_open(struct input_dev *dev) @@ -2679,7 +2741,7 @@ static ssize_t hotkey_bios_enabled_show(struct device *dev, struct device_attribute *attr, char *buf) { - return sprintf(buf, "0\n"); + return sysfs_emit(buf, "0\n"); } static DEVICE_ATTR_RO(hotkey_bios_enabled); @@ -3044,11 +3106,8 @@ static void tpacpi_send_radiosw_update(void) static void hotkey_exit(void) { -#ifdef CONFIG_THINKPAD_ACPI_HOTKEY_POLL mutex_lock(&hotkey_mutex); hotkey_poll_stop_sync(); - mutex_unlock(&hotkey_mutex); -#endif 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 @@ -3057,15 +3116,8 @@ static void hotkey_exit(void) hotkey_mask_set(hotkey_orig_mask)) | hotkey_status_set(false)) != 0) pr_err("failed to restore hot key mask to BIOS defaults\n"); -} -static void __init hotkey_unmap(const unsigned int scancode) -{ - if (hotkey_keycode_map[scancode] != KEY_RESERVED) { - clear_bit(hotkey_keycode_map[scancode], - tpacpi_inputdev->keybit); - hotkey_keycode_map[scancode] = KEY_RESERVED; - } + mutex_unlock(&hotkey_mutex); } /* @@ -3097,9 +3149,6 @@ 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 hotkey_init_tablet_mode(void) { int in_tablet_mode = 0, res; @@ -3136,209 +3185,127 @@ static int hotkey_init_tablet_mode(void) return in_tablet_mode; } -static int __init hotkey_init(struct ibm_init_struct *iibm) -{ - /* Requirements for changing the default keymaps: - * - * 1. Many of the keys are mapped to KEY_RESERVED for very - * good reasons. Do not change them unless you have deep - * knowledge on the IBM and Lenovo ThinkPad firmware for - * the various ThinkPad models. The driver behaves - * differently for KEY_RESERVED: such keys have their - * hot key mask *unset* in mask_recommended, and also - * in the initial hot key mask programmed into the - * firmware at driver load time, which means the firm- - * ware may react very differently if you change them to - * something else; - * - * 2. You must be subscribed to the linux-thinkpad and - * ibm-acpi-devel mailing lists, and you should read the - * list archives since 2007 if you want to change the - * keymaps. This requirement exists so that you will - * know the past history of problems with the thinkpad- - * acpi driver keymaps, and also that you will be - * listening to any bug reports; - * - * 3. Do not send thinkpad-acpi specific patches directly to - * for merging, *ever*. Send them to the linux-acpi - * mailinglist for comments. Merging is to be done only - * through acpi-test and the ACPI maintainer. - * - * If the above is too much to ask, don't change the keymap. - * Ask the thinkpad-acpi maintainer to do it, instead. +static const struct key_entry keymap_ibm[] __initconst = { + /* Original hotkey mappings translated scancodes 0x00 - 0x1f */ + { KE_KEY, TP_ACPI_HOTKEYSCAN_FNF1, { KEY_FN_F1 } }, + { KE_KEY, TP_ACPI_HOTKEYSCAN_FNF2, { KEY_BATTERY } }, + { KE_KEY, TP_ACPI_HOTKEYSCAN_FNF3, { KEY_COFFEE } }, + { KE_KEY, TP_ACPI_HOTKEYSCAN_FNF4, { KEY_SLEEP } }, + { KE_KEY, TP_ACPI_HOTKEYSCAN_FNF5, { KEY_WLAN } }, + { KE_KEY, TP_ACPI_HOTKEYSCAN_FNF6, { KEY_FN_F6 } }, + { KE_KEY, TP_ACPI_HOTKEYSCAN_FNF7, { KEY_SWITCHVIDEOMODE } }, + { KE_KEY, TP_ACPI_HOTKEYSCAN_FNF8, { KEY_FN_F8 } }, + { KE_KEY, TP_ACPI_HOTKEYSCAN_FNF9, { KEY_FN_F9 } }, + { KE_KEY, TP_ACPI_HOTKEYSCAN_FNF10, { KEY_FN_F10 } }, + { KE_KEY, TP_ACPI_HOTKEYSCAN_FNF11, { KEY_FN_F11 } }, + { KE_KEY, TP_ACPI_HOTKEYSCAN_FNF12, { KEY_SUSPEND } }, + /* Brightness: firmware always reacts, suppressed through hotkey_reserved_mask. */ + { KE_KEY, TP_ACPI_HOTKEYSCAN_FNHOME, { KEY_BRIGHTNESSUP } }, + { KE_KEY, TP_ACPI_HOTKEYSCAN_FNEND, { KEY_BRIGHTNESSDOWN } }, + /* Thinklight: firmware always reacts, suppressed through hotkey_reserved_mask. */ + { KE_KEY, TP_ACPI_HOTKEYSCAN_FNPAGEUP, { KEY_KBDILLUMTOGGLE } }, + { KE_KEY, TP_ACPI_HOTKEYSCAN_FNSPACE, { KEY_ZOOM } }, + /* + * Volume: firmware always reacts and reprograms the built-in *extra* mixer. + * Suppressed by default through hotkey_reserved_mask. + */ + { KE_KEY, TP_ACPI_HOTKEYSCAN_VOLUMEUP, { KEY_VOLUMEUP } }, + { KE_KEY, TP_ACPI_HOTKEYSCAN_VOLUMEDOWN, { KEY_VOLUMEDOWN } }, + { KE_KEY, TP_ACPI_HOTKEYSCAN_MUTE, { KEY_MUTE } }, + { KE_KEY, TP_ACPI_HOTKEYSCAN_THINKPAD, { KEY_VENDOR } }, + { KE_END } +}; + +static const struct key_entry keymap_lenovo[] __initconst = { + /* Original hotkey mappings translated scancodes 0x00 - 0x1f */ + { KE_KEY, TP_ACPI_HOTKEYSCAN_FNF1, { KEY_FN_F1 } }, + { KE_KEY, TP_ACPI_HOTKEYSCAN_FNF2, { KEY_COFFEE } }, + { KE_KEY, TP_ACPI_HOTKEYSCAN_FNF3, { KEY_BATTERY } }, + { KE_KEY, TP_ACPI_HOTKEYSCAN_FNF4, { KEY_SLEEP } }, + { KE_KEY, TP_ACPI_HOTKEYSCAN_FNF5, { KEY_WLAN } }, + { KE_KEY, TP_ACPI_HOTKEYSCAN_FNF6, { KEY_CAMERA, } }, + { KE_KEY, TP_ACPI_HOTKEYSCAN_FNF7, { KEY_SWITCHVIDEOMODE } }, + { KE_KEY, TP_ACPI_HOTKEYSCAN_FNF8, { KEY_FN_F8 } }, + { KE_KEY, TP_ACPI_HOTKEYSCAN_FNF9, { KEY_FN_F9 } }, + { KE_KEY, TP_ACPI_HOTKEYSCAN_FNF10, { KEY_FN_F10 } }, + { KE_KEY, TP_ACPI_HOTKEYSCAN_FNF11, { KEY_FN_F11 } }, + { KE_KEY, TP_ACPI_HOTKEYSCAN_FNF12, { KEY_SUSPEND } }, + /* + * These should be enabled --only-- when ACPI video is disabled and + * are handled in a special way by the init code. + */ + { KE_KEY, TP_ACPI_HOTKEYSCAN_FNHOME, { KEY_BRIGHTNESSUP } }, + { KE_KEY, TP_ACPI_HOTKEYSCAN_FNEND, { KEY_BRIGHTNESSDOWN } }, + /* Suppressed by default through hotkey_reserved_mask. */ + { KE_KEY, TP_ACPI_HOTKEYSCAN_FNPAGEUP, { KEY_KBDILLUMTOGGLE } }, + { KE_KEY, TP_ACPI_HOTKEYSCAN_FNSPACE, { KEY_ZOOM } }, + /* + * Volume: z60/z61, T60 (BIOS version?): firmware always reacts and + * reprograms the built-in *extra* mixer. + * T60?, T61, R60?, R61: firmware and EC tries to send these over + * the regular keyboard (not through tpacpi). There are still weird bugs + * re. MUTE. May cause the BIOS to interfere with the HDA mixer. + * Suppressed by default through hotkey_reserved_mask. */ + { KE_KEY, TP_ACPI_HOTKEYSCAN_VOLUMEUP, { KEY_VOLUMEUP } }, + { KE_KEY, TP_ACPI_HOTKEYSCAN_VOLUMEDOWN, { KEY_VOLUMEDOWN } }, + { KE_KEY, TP_ACPI_HOTKEYSCAN_MUTE, { KEY_MUTE } }, + { KE_KEY, TP_ACPI_HOTKEYSCAN_THINKPAD, { KEY_VENDOR } }, + { KE_KEY, TP_ACPI_HOTKEYSCAN_MICMUTE, { KEY_MICMUTE } }, + { KE_KEY, TP_ACPI_HOTKEYSCAN_CONFIG, { KEY_CONFIG } }, + { KE_KEY, TP_ACPI_HOTKEYSCAN_SEARCH, { KEY_SEARCH } }, + { KE_KEY, TP_ACPI_HOTKEYSCAN_SCALE, { KEY_SCALE } }, + { KE_KEY, TP_ACPI_HOTKEYSCAN_FILE, { KEY_FILE } }, + /* Adaptive keyboard mappings for Carbon X1 2014 translated scancodes 0x20 - 0x33 */ + { KE_KEY, TP_ACPI_HOTKEYSCAN_MUTE2, { KEY_RESERVED } }, + { KE_KEY, TP_ACPI_HOTKEYSCAN_BRIGHTNESS_ZERO, { KEY_BRIGHTNESS_MIN } }, + { KE_KEY, TP_ACPI_HOTKEYSCAN_CLIPPING_TOOL, { KEY_SELECTIVE_SCREENSHOT } }, + { KE_KEY, TP_ACPI_HOTKEYSCAN_CLOUD, { KEY_XFER } }, + { KE_KEY, TP_ACPI_HOTKEYSCAN_UNK9, { KEY_RESERVED } }, + { KE_KEY, TP_ACPI_HOTKEYSCAN_VOICE, { KEY_VOICECOMMAND } }, + { KE_KEY, TP_ACPI_HOTKEYSCAN_UNK10, { KEY_RESERVED } }, + { KE_KEY, TP_ACPI_HOTKEYSCAN_GESTURES, { KEY_RESERVED } }, + { KE_KEY, TP_ACPI_HOTKEYSCAN_UNK11, { KEY_RESERVED } }, + { KE_KEY, TP_ACPI_HOTKEYSCAN_UNK12, { KEY_RESERVED } }, + { KE_KEY, TP_ACPI_HOTKEYSCAN_UNK13, { KEY_RESERVED } }, + { KE_KEY, TP_ACPI_HOTKEYSCAN_CONFIG2, { KEY_CONFIG } }, + { KE_KEY, TP_ACPI_HOTKEYSCAN_NEW_TAB, { KEY_RESERVED } }, + { KE_KEY, TP_ACPI_HOTKEYSCAN_RELOAD, { KEY_REFRESH } }, + { KE_KEY, TP_ACPI_HOTKEYSCAN_BACK, { KEY_BACK } }, + { KE_KEY, TP_ACPI_HOTKEYSCAN_MIC_DOWN, { KEY_RESERVED } }, + { KE_KEY, TP_ACPI_HOTKEYSCAN_MIC_UP, { KEY_RESERVED } }, + { KE_KEY, TP_ACPI_HOTKEYSCAN_MIC_CANCELLATION, { KEY_RESERVED } }, + { KE_KEY, TP_ACPI_HOTKEYSCAN_CAMERA_MODE, { KEY_RESERVED } }, + { KE_KEY, TP_ACPI_HOTKEYSCAN_ROTATE_DISPLAY, { KEY_RESERVED } }, + /* Extended hotkeys mappings translated scancodes 0x34 - 0x4d */ + { KE_KEY, TP_ACPI_HOTKEYSCAN_STAR, { KEY_BOOKMARKS } }, + { KE_KEY, TP_ACPI_HOTKEYSCAN_CLIPPING_TOOL2, { KEY_SELECTIVE_SCREENSHOT } }, + { KE_KEY, TP_ACPI_HOTKEYSCAN_CALCULATOR, { KEY_CALC } }, + { KE_KEY, TP_ACPI_HOTKEYSCAN_BLUETOOTH, { KEY_BLUETOOTH } }, + { KE_KEY, TP_ACPI_HOTKEYSCAN_KEYBOARD, { KEY_KEYBOARD } }, + /* Used by "Lenovo Quick Clean" */ + { KE_KEY, TP_ACPI_HOTKEYSCAN_FN_RIGHT_SHIFT, { KEY_FN_RIGHT_SHIFT } }, + { KE_KEY, TP_ACPI_HOTKEYSCAN_NOTIFICATION_CENTER, { KEY_NOTIFICATION_CENTER } }, + { KE_KEY, TP_ACPI_HOTKEYSCAN_PICKUP_PHONE, { KEY_PICKUP_PHONE } }, + { KE_KEY, TP_ACPI_HOTKEYSCAN_HANGUP_PHONE, { KEY_HANGUP_PHONE } }, + /* + * All mapping below are for raw untranslated hkey event codes mapped directly + * after switching to sparse keymap support. The mappings above use translated + * scancodes to preserve uAPI compatibility, see tpacpi_input_send_key(). + */ + { KE_KEY, 0x131d, { KEY_VENDOR } }, /* System debug info, similar to old ThinkPad key */ + { KE_KEY, 0x1320, { KEY_LINK_PHONE } }, + { KE_KEY, TP_HKEY_EV_TRACK_DOUBLETAP /* 0x8036 */, { KEY_PROG4 } }, + { KE_END } +}; +static int __init hotkey_init(struct ibm_init_struct *iibm) +{ 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_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, - - /* Scan codes 0x0C to 0x1F: Other ACPI HKEY hot keys */ - KEY_UNKNOWN, /* 0x0C: FN+BACKSPACE */ - KEY_UNKNOWN, /* 0x0D: FN+INSERT */ - KEY_UNKNOWN, /* 0x0E: FN+DELETE */ - - /* brightness: firmware always reacts to them */ - KEY_RESERVED, /* 0x0F: FN+HOME (brightness up) */ - KEY_RESERVED, /* 0x10: FN+END (brightness down) */ - - /* Thinklight: firmware always react to it */ - KEY_RESERVED, /* 0x11: FN+PGUP (thinklight toggle) */ - - KEY_UNKNOWN, /* 0x12: FN+PGDOWN */ - KEY_ZOOM, /* 0x13: FN+SPACE (zoom) */ - - /* Volume: firmware always react to it and reprograms - * the built-in *extra* mixer. Never map it to control - * another mixer by default. */ - KEY_RESERVED, /* 0x14: VOLUME UP */ - KEY_RESERVED, /* 0x15: VOLUME DOWN */ - KEY_RESERVED, /* 0x16: MUTE */ - - 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, - - /* No assignments, only used for Adaptive keyboards. */ - KEY_UNKNOWN, KEY_UNKNOWN, KEY_UNKNOWN, KEY_UNKNOWN, - KEY_UNKNOWN, KEY_UNKNOWN, KEY_UNKNOWN, KEY_UNKNOWN, - KEY_UNKNOWN, KEY_UNKNOWN, KEY_UNKNOWN, KEY_UNKNOWN, - KEY_UNKNOWN, KEY_UNKNOWN, KEY_UNKNOWN, KEY_UNKNOWN, - KEY_UNKNOWN, KEY_UNKNOWN, KEY_UNKNOWN, - - /* No assignment, used for newer Lenovo models */ - KEY_UNKNOWN, KEY_UNKNOWN, KEY_UNKNOWN, KEY_UNKNOWN, - KEY_UNKNOWN, KEY_UNKNOWN, KEY_UNKNOWN, KEY_UNKNOWN, - KEY_UNKNOWN, KEY_UNKNOWN, KEY_UNKNOWN, KEY_UNKNOWN, - KEY_UNKNOWN, KEY_UNKNOWN, KEY_UNKNOWN, KEY_UNKNOWN, - KEY_UNKNOWN, KEY_UNKNOWN, KEY_UNKNOWN, KEY_UNKNOWN, - KEY_UNKNOWN, KEY_UNKNOWN - - }, - - /* 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_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 */ - KEY_UNKNOWN, /* 0x0C: FN+BACKSPACE */ - KEY_UNKNOWN, /* 0x0D: FN+INSERT */ - KEY_UNKNOWN, /* 0x0E: FN+DELETE */ - - /* These should be enabled --only-- when ACPI video - * is disabled (i.e. in "vendor" mode), and are handled - * in a special way by the init code */ - KEY_BRIGHTNESSUP, /* 0x0F: FN+HOME (brightness up) */ - KEY_BRIGHTNESSDOWN, /* 0x10: FN+END (brightness down) */ - - KEY_RESERVED, /* 0x11: FN+PGUP (thinklight toggle) */ - - KEY_UNKNOWN, /* 0x12: FN+PGDOWN */ - KEY_ZOOM, /* 0x13: FN+SPACE (zoom) */ - - /* Volume: z60/z61, T60 (BIOS version?): firmware always - * react to it and reprograms the built-in *extra* mixer. - * Never map it to control another mixer by default. - * - * T60?, T61, R60?, R61: firmware and EC tries to send - * these over the regular keyboard, so these are no-ops, - * but there are still weird bugs re. MUTE, so do not - * change unless you get test reports from all Lenovo - * models. May cause the BIOS to interfere with the - * HDA mixer. - */ - KEY_RESERVED, /* 0x14: VOLUME UP */ - KEY_RESERVED, /* 0x15: VOLUME DOWN */ - KEY_RESERVED, /* 0x16: MUTE */ - - KEY_VENDOR, /* 0x17: Thinkpad/AccessIBM/Lenovo */ - - /* (assignments unknown, please report if found) */ - 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_FILE, - - /* - * These are the adaptive keyboard keycodes for Carbon X1 2014. - * The first item in this list is the Mute button which is - * emitted with 0x103 through - * adaptive_keyboard_hotkey_notify_hotkey() when the sound - * symbol is held. - * We'll need to offset those by 0x20. - */ - KEY_RESERVED, /* Mute held, 0x103 */ - KEY_BRIGHTNESS_MIN, /* Backlight off */ - KEY_RESERVED, /* Clipping tool */ - KEY_RESERVED, /* Cloud */ - KEY_RESERVED, - KEY_VOICECOMMAND, /* Voice */ - KEY_RESERVED, - KEY_RESERVED, /* Gestures */ - KEY_RESERVED, - KEY_RESERVED, - KEY_RESERVED, - KEY_CONFIG, /* Settings */ - KEY_RESERVED, /* New tab */ - KEY_REFRESH, /* Reload */ - KEY_BACK, /* Back */ - KEY_RESERVED, /* Microphone down */ - KEY_RESERVED, /* Microphone up */ - KEY_RESERVED, /* Microphone cancellation */ - KEY_RESERVED, /* Camera mode */ - KEY_RESERVED, /* Rotate display, 0x116 */ - - /* - * These are found in 2017 models (e.g. T470s, X270). - * The lowest known value is 0x311, which according to - * the manual should launch a user defined favorite - * application. - * - * The offset for these is TP_ACPI_HOTKEYSCAN_EXTENDED_START, - * corresponding to 0x34. - */ - - /* (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, KEY_UNKNOWN, KEY_UNKNOWN, - KEY_UNKNOWN, KEY_UNKNOWN, KEY_UNKNOWN, KEY_UNKNOWN, - KEY_UNKNOWN, - - KEY_BOOKMARKS, /* Favorite app, 0x311 */ - KEY_SELECTIVE_SCREENSHOT, /* Clipping tool */ - KEY_CALC, /* Calculator (above numpad, P52) */ - KEY_BLUETOOTH, /* Bluetooth */ - KEY_KEYBOARD, /* Keyboard, 0x315 */ - KEY_FN_RIGHT_SHIFT, /* Fn + right Shift */ - KEY_NOTIFICATION_CENTER, /* Notification Center */ - KEY_PICKUP_PHONE, /* Answer incoming call */ - KEY_HANGUP_PHONE, /* Decline incoming call */ - }, - }; - static const struct tpacpi_quirk tpacpi_keymap_qtable[] __initconst = { /* Generic maps (fallback) */ { @@ -3353,17 +3320,11 @@ static int __init hotkey_init(struct ibm_init_struct *iibm) }, }; -#define TPACPI_HOTKEY_MAP_SIZE sizeof(tpacpi_keymap_t) -#define TPACPI_HOTKEY_MAP_TYPESIZE sizeof(tpacpi_keymap_entry_t) - - int res, i; - int status; - int hkeyv; + unsigned long keymap_id, quirks; + const struct key_entry *keymap; bool radiosw_state = false; bool tabletsw_state = false; - - unsigned long quirks; - unsigned long keymap_id; + int hkeyv, res, status, camera_shutter_state; vdbg_printk(TPACPI_DBG_INIT | TPACPI_DBG_HKEY, "initializing hotkey subdriver\n"); @@ -3503,29 +3464,34 @@ static int __init hotkey_init(struct ibm_init_struct *iibm) /* Set up key map */ 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); - hotkey_keycode_map = kmemdup(&tpacpi_keymaps[keymap_id], - TPACPI_HOTKEY_MAP_SIZE, GFP_KERNEL); - if (!hotkey_keycode_map) { - pr_err("failed to allocate memory for key map\n"); - return -ENOMEM; + /* Keys which should be reserved on both IBM and Lenovo models */ + hotkey_reserved_mask = TP_ACPI_HKEY_KBD_LIGHT_MASK | + TP_ACPI_HKEY_VOLUP_MASK | + TP_ACPI_HKEY_VOLDWN_MASK | + TP_ACPI_HKEY_MUTE_MASK; + /* + * Reserve brightness up/down unconditionally on IBM models, on Lenovo + * models these are disabled based on acpi_video_get_backlight_type(). + */ + if (keymap_id == TPACPI_KEYMAP_IBM_GENERIC) { + hotkey_reserved_mask |= TP_ACPI_HKEY_BRGHTUP_MASK | + TP_ACPI_HKEY_BRGHTDWN_MASK; + keymap = keymap_ibm; + } else { + keymap = keymap_lenovo; } - input_set_capability(tpacpi_inputdev, EV_MSC, MSC_SCAN); - tpacpi_inputdev->keycodesize = TPACPI_HOTKEY_MAP_TYPESIZE; - tpacpi_inputdev->keycodemax = TPACPI_HOTKEY_MAP_LEN; - tpacpi_inputdev->keycode = hotkey_keycode_map; - for (i = 0; i < TPACPI_HOTKEY_MAP_LEN; i++) { - if (hotkey_keycode_map[i] != KEY_RESERVED) { - input_set_capability(tpacpi_inputdev, EV_KEY, - hotkey_keycode_map[i]); - } else { - if (i < sizeof(hotkey_reserved_mask)*8) - hotkey_reserved_mask |= 1 << i; - } + res = sparse_keymap_setup(tpacpi_inputdev, keymap, NULL); + if (res) + return res; + + camera_shutter_state = get_camera_shutter(); + if (camera_shutter_state >= 0) { + input_set_capability(tpacpi_inputdev, EV_SW, SW_CAMERA_LENS_COVER); + input_report_switch(tpacpi_inputdev, SW_CAMERA_LENS_COVER, camera_shutter_state); } if (tp_features.hotkey_wlsw) { @@ -3549,11 +3515,8 @@ static int __init hotkey_init(struct ibm_init_struct *iibm) /* Disable brightness up/down on Lenovo thinkpads when * ACPI is handling them, otherwise it is plain impossible * for userspace to do something even remotely sane */ - hotkey_reserved_mask |= - (1 << TP_ACPI_HOTKEYSCAN_FNHOME) - | (1 << TP_ACPI_HOTKEYSCAN_FNEND); - hotkey_unmap(TP_ACPI_HOTKEYSCAN_FNHOME); - hotkey_unmap(TP_ACPI_HOTKEYSCAN_FNEND); + hotkey_reserved_mask |= TP_ACPI_HKEY_BRGHTUP_MASK | + TP_ACPI_HKEY_BRGHTDWN_MASK; } #ifdef CONFIG_THINKPAD_ACPI_HOTKEY_POLL @@ -3593,6 +3556,9 @@ static int __init hotkey_init(struct ibm_init_struct *iibm) hotkey_poll_setup_safe(true); + /* Enable doubletap by default */ + tp_features.trackpoint_doubletap = 1; + return 0; } @@ -3610,10 +3576,6 @@ static const int adaptive_keyboard_modes[] = { FUNCTION_MODE }; -#define DFR_CHANGE_ROW 0x101 -#define DFR_SHOW_QUICKVIEW_ROW 0x102 -#define FIRST_ADAPTIVE_KEY 0x103 - /* press Fn key a while second, it will switch to Function Mode. Then * release Fn key, previous mode be restored. */ @@ -3664,153 +3626,67 @@ static int adaptive_keyboard_get_next_mode(int mode) return adaptive_keyboard_modes[i]; } -static bool adaptive_keyboard_hotkey_notify_hotkey(unsigned int scancode) +static void adaptive_keyboard_change_row(void) { - int current_mode = 0; - int new_mode = 0; - int keycode; - - 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 { - current_mode = adaptive_keyboard_get_mode(); - if (current_mode < 0) - return false; - new_mode = adaptive_keyboard_get_next_mode( - current_mode); - } - - if (adaptive_keyboard_set_mode(new_mode) < 0) - return false; + int mode; - return true; - - case DFR_SHOW_QUICKVIEW_ROW: - current_mode = adaptive_keyboard_get_mode(); - if (current_mode < 0) - return false; - - adaptive_keyboard_prev_mode = current_mode; - adaptive_keyboard_mode_is_saved = true; - - if (adaptive_keyboard_set_mode (FUNCTION_MODE) < 0) - return false; - return true; - - default: - if (scancode < FIRST_ADAPTIVE_KEY || - scancode >= FIRST_ADAPTIVE_KEY + - TP_ACPI_HOTKEYSCAN_EXTENDED_START - - TP_ACPI_HOTKEYSCAN_ADAPTIVE_START) { - pr_info("Unhandled adaptive keyboard key: 0x%x\n", - scancode); - return false; - } - keycode = hotkey_keycode_map[scancode - FIRST_ADAPTIVE_KEY + - TP_ACPI_HOTKEYSCAN_ADAPTIVE_START]; - if (keycode != KEY_RESERVED) { - mutex_lock(&tpacpi_inputdev_send_mutex); - - input_report_key(tpacpi_inputdev, keycode, 1); - input_sync(tpacpi_inputdev); - - input_report_key(tpacpi_inputdev, keycode, 0); - input_sync(tpacpi_inputdev); - - mutex_unlock(&tpacpi_inputdev_send_mutex); - } - return true; + if (adaptive_keyboard_mode_is_saved) { + mode = adaptive_keyboard_prev_mode; + adaptive_keyboard_mode_is_saved = false; + } else { + mode = adaptive_keyboard_get_mode(); + if (mode < 0) + return; + mode = adaptive_keyboard_get_next_mode(mode); } + + adaptive_keyboard_set_mode(mode); } -static bool hotkey_notify_extended_hotkey(const u32 hkey) +static void adaptive_keyboard_s_quickview_row(void) { - unsigned int scancode; + int mode; - switch (hkey) { - case TP_HKEY_EV_PRIVACYGUARD_TOGGLE: - case TP_HKEY_EV_AMT_TOGGLE: - case TP_HKEY_EV_PROFILE_TOGGLE: - tpacpi_driver_event(hkey); - return true; - } + mode = adaptive_keyboard_get_mode(); + if (mode < 0) + return; - /* Extended keycodes start at 0x300 and our offset into the map - * TP_ACPI_HOTKEYSCAN_EXTENDED_START. The calculated scancode - * will be positive, but might not be in the correct range. - */ - scancode = (hkey & 0xfff) - (0x300 - TP_ACPI_HOTKEYSCAN_EXTENDED_START); - if (scancode >= TP_ACPI_HOTKEYSCAN_EXTENDED_START && - scancode < TPACPI_HOTKEY_MAP_LEN) { - tpacpi_input_send_key(scancode); - return true; - } + adaptive_keyboard_prev_mode = mode; + adaptive_keyboard_mode_is_saved = true; - return false; + adaptive_keyboard_set_mode(FUNCTION_MODE); } -static bool hotkey_notify_hotkey(const u32 hkey, - bool *send_acpi_ev, - bool *ignore_acpi_ev) +/* 0x1000-0x1FFF: key presses */ +static bool hotkey_notify_hotkey(const u32 hkey, bool *send_acpi_ev) { - /* 0x1000-0x1FFF: key presses */ - unsigned int scancode = hkey & 0xfff; - *send_acpi_ev = true; - *ignore_acpi_ev = false; + /* Never send ACPI netlink events for original hotkeys (hkey: 0x1001 - 0x1020) */ + if (hkey >= TP_HKEY_EV_ORIG_KEY_START && hkey <= TP_HKEY_EV_ORIG_KEY_END) { + *send_acpi_ev = false; - /* - * Original events are in the 0x10XX range, the adaptive keyboard - * found in 2014 X1 Carbon emits events are of 0x11XX. In 2017 - * models, additional keys are emitted through 0x13XX. - */ - switch ((hkey >> 8) & 0xf) { - case 0: - if (scancode > 0 && - scancode <= TP_ACPI_HOTKEYSCAN_ADAPTIVE_START) { - /* HKEY event 0x1001 is scancode 0x00 */ - scancode--; - if (!(hotkey_source_mask & (1 << scancode))) { - tpacpi_input_send_key_masked(scancode); - *send_acpi_ev = false; - } else { - *ignore_acpi_ev = true; - } + /* Original hotkeys may be polled from NVRAM instead */ + unsigned int scancode = hkey - TP_HKEY_EV_ORIG_KEY_START; + if (hotkey_source_mask & (1 << scancode)) return true; - } - break; - - case 1: - return adaptive_keyboard_hotkey_notify_hotkey(scancode); - - case 3: - return hotkey_notify_extended_hotkey(hkey); } - return false; + return tpacpi_input_send_key(hkey, send_acpi_ev); } -static bool hotkey_notify_wakeup(const u32 hkey, - bool *send_acpi_ev, - bool *ignore_acpi_ev) +/* 0x2000-0x2FFF: Wakeup reason */ +static bool hotkey_notify_wakeup(const u32 hkey, bool *send_acpi_ev) { - /* 0x2000-0x2FFF: Wakeup reason */ - *send_acpi_ev = true; - *ignore_acpi_ev = false; - switch (hkey) { case TP_HKEY_EV_WKUP_S3_UNDOCK: /* suspend, undock */ case TP_HKEY_EV_WKUP_S4_UNDOCK: /* hibernation, undock */ hotkey_wakeup_reason = TP_ACPI_WAKEUP_UNDOCK; - *ignore_acpi_ev = true; + *send_acpi_ev = false; break; case TP_HKEY_EV_WKUP_S3_BAYEJ: /* suspend, bay eject */ case TP_HKEY_EV_WKUP_S4_BAYEJ: /* hibernation, bay eject */ hotkey_wakeup_reason = TP_ACPI_WAKEUP_BAYEJ; - *ignore_acpi_ev = true; + *send_acpi_ev = false; break; case TP_HKEY_EV_WKUP_S3_BATLOW: /* Battery on critical low level/S3 */ @@ -3832,14 +3708,9 @@ static bool hotkey_notify_wakeup(const u32 hkey, return true; } -static bool hotkey_notify_dockevent(const u32 hkey, - bool *send_acpi_ev, - bool *ignore_acpi_ev) +/* 0x4000-0x4FFF: dock-related events */ +static bool hotkey_notify_dockevent(const u32 hkey, bool *send_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 */ @@ -3869,7 +3740,6 @@ static bool hotkey_notify_dockevent(const u32 hkey, case TP_HKEY_EV_KBD_COVER_ATTACH: case TP_HKEY_EV_KBD_COVER_DETACH: *send_acpi_ev = false; - *ignore_acpi_ev = true; return true; default: @@ -3877,14 +3747,9 @@ static bool hotkey_notify_dockevent(const u32 hkey, } } -static bool hotkey_notify_usrevent(const u32 hkey, - bool *send_acpi_ev, - bool *ignore_acpi_ev) +/* 0x5000-0x5FFF: human interface helpers */ +static bool hotkey_notify_usrevent(const u32 hkey, bool *send_acpi_ev) { - /* 0x5000-0x5FFF: human interface helpers */ - *send_acpi_ev = true; - *ignore_acpi_ev = false; - switch (hkey) { case TP_HKEY_EV_PEN_INSERTED: /* X61t: tablet pen inserted into bay */ case TP_HKEY_EV_PEN_REMOVED: /* X61t: tablet pen removed from bay */ @@ -3901,7 +3766,7 @@ static bool hotkey_notify_usrevent(const u32 hkey, case TP_HKEY_EV_LID_OPEN: /* Lid opened */ case TP_HKEY_EV_BRGHT_CHANGED: /* brightness changed */ /* do not propagate these events */ - *ignore_acpi_ev = true; + *send_acpi_ev = false; return true; default: @@ -3912,14 +3777,9 @@ static bool hotkey_notify_usrevent(const u32 hkey, static void thermal_dump_all_sensors(void); static void palmsensor_refresh(void); -static bool hotkey_notify_6xxx(const u32 hkey, - bool *send_acpi_ev, - bool *ignore_acpi_ev) +/* 0x6000-0x6FFF: thermal alarms/notices and keyboard events */ +static bool hotkey_notify_6xxx(const u32 hkey, bool *send_acpi_ev) { - /* 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: pr_debug("EC reports: Thermal Table has changed\n"); @@ -3944,6 +3804,10 @@ static bool hotkey_notify_6xxx(const u32 hkey, pr_alert("THERMAL EMERGENCY: battery is extremely hot!\n"); /* recommended action: immediate sleep/hibernate */ break; + case TP_HKEY_EV_ALARM_BAT_LIM_CHANGE: + pr_debug("Battery Info: battery charge threshold changed\n"); + /* User changed charging threshold. No action needed */ + return true; case TP_HKEY_EV_ALARM_SENSOR_HOT: pr_crit("THERMAL ALARM: a sensor reports something is too hot!\n"); /* recommended action: warn user through gui, that */ @@ -3965,14 +3829,12 @@ static bool hotkey_notify_6xxx(const u32 hkey, /* 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; case TP_HKEY_EV_KEY_FN_ESC: /* Get the media key status to force the status LED to update */ acpi_evalf(hkey_handle, NULL, "GMKS", "v"); *send_acpi_ev = false; - *ignore_acpi_ev = true; return true; case TP_HKEY_EV_TABLET_CHANGED: @@ -3996,11 +3858,23 @@ static bool hotkey_notify_6xxx(const u32 hkey, return true; } +static bool hotkey_notify_8xxx(const u32 hkey, bool *send_acpi_ev) +{ + switch (hkey) { + case TP_HKEY_EV_TRACK_DOUBLETAP: + if (tp_features.trackpoint_doubletap) + tpacpi_input_send_key(hkey, send_acpi_ev); + + return true; + default: + return false; + } +} + static void hotkey_notify(struct ibm_struct *ibm, u32 event) { u32 hkey; bool send_acpi_ev; - bool ignore_acpi_ev; bool known_ev; if (event != 0x80) { @@ -4025,18 +3899,16 @@ static void hotkey_notify(struct ibm_struct *ibm, u32 event) } send_acpi_ev = true; - ignore_acpi_ev = false; + known_ev = false; switch (hkey >> 12) { case 1: /* 0x1000-0x1FFF: key presses */ - known_ev = hotkey_notify_hotkey(hkey, &send_acpi_ev, - &ignore_acpi_ev); + known_ev = hotkey_notify_hotkey(hkey, &send_acpi_ev); break; case 2: /* 0x2000-0x2FFF: Wakeup reason */ - known_ev = hotkey_notify_wakeup(hkey, &send_acpi_ev, - &ignore_acpi_ev); + known_ev = hotkey_notify_wakeup(hkey, &send_acpi_ev); break; case 3: /* 0x3000-0x3FFF: bay-related wakeups */ @@ -4051,38 +3923,34 @@ static void hotkey_notify(struct ibm_struct *ibm, u32 event) /* FIXME: kick libata if SATA link offline */ known_ev = true; break; - default: - known_ev = false; } break; case 4: /* 0x4000-0x4FFF: dock-related events */ - known_ev = hotkey_notify_dockevent(hkey, &send_acpi_ev, - &ignore_acpi_ev); + known_ev = hotkey_notify_dockevent(hkey, &send_acpi_ev); break; case 5: /* 0x5000-0x5FFF: human interface helpers */ - known_ev = hotkey_notify_usrevent(hkey, &send_acpi_ev, - &ignore_acpi_ev); + known_ev = hotkey_notify_usrevent(hkey, &send_acpi_ev); break; case 6: /* 0x6000-0x6FFF: thermal alarms/notices and * keyboard events */ - known_ev = hotkey_notify_6xxx(hkey, &send_acpi_ev, - &ignore_acpi_ev); + known_ev = hotkey_notify_6xxx(hkey, &send_acpi_ev); break; case 7: /* 0x7000-0x7FFF: misc */ if (tp_features.hotkey_wlsw && hkey == TP_HKEY_EV_RFKILL_CHANGED) { tpacpi_send_radiosw_update(); - send_acpi_ev = 0; + send_acpi_ev = false; known_ev = true; - break; } - fallthrough; /* to default */ - default: - known_ev = false; + break; + case 8: + /* 0x8000-0x8FFF: misc2 */ + known_ev = hotkey_notify_8xxx(hkey, &send_acpi_ev); + break; } if (!known_ev) { pr_notice("unhandled HKEY event 0x%04x\n", hkey); @@ -4091,7 +3959,7 @@ static void hotkey_notify(struct ibm_struct *ibm, u32 event) } /* netlink events */ - if (!ignore_acpi_ev && send_acpi_ev) { + if (send_acpi_ev) { acpi_bus_generate_netlink_event( ibm->acpi->device->pnp.device_class, dev_name(&ibm->acpi->device->dev), @@ -7579,10 +7447,8 @@ static int __init volume_create_alsa_mixer(void) data = card->private_data; data->card = card; - strscpy(card->driver, TPACPI_ALSA_DRVNAME, - sizeof(card->driver)); - strscpy(card->shortname, TPACPI_ALSA_SHRTNAME, - sizeof(card->shortname)); + strscpy(card->driver, TPACPI_ALSA_DRVNAME); + strscpy(card->shortname, TPACPI_ALSA_SHRTNAME); snprintf(card->mixername, sizeof(card->mixername), "ThinkPad EC %s", (thinkpad_id.ec_version_str) ? thinkpad_id.ec_version_str : "(unknown)"); @@ -7914,6 +7780,28 @@ static struct ibm_struct volume_driver_data = { * EC 0x2f (HFSP) might be available *for reading*, but do not use * it for writing. * + * TPACPI_FAN_RD_ACPI_FANG: + * ACPI FANG method: returns fan control register + * + * Takes one parameter which is 0x8100 plus the offset to EC memory + * address 0xf500 and returns the byte at this address. + * + * 0xf500: + * When the value is less than 9 automatic mode is enabled + * 0xf502: + * Contains the current fan speed from 0-100% + * 0xf506: + * Bit 7 has to be set in order to enable manual control by + * writing a value >= 9 to 0xf500 + * + * TPACPI_FAN_WR_ACPI_FANW: + * ACPI FANW method: sets fan control registers + * + * Takes 0x8100 plus the offset to EC memory address 0xf500 and the + * value to be written there as parameters. + * + * see TPACPI_FAN_RD_ACPI_FANG + * * TPACPI_FAN_WR_TPEC: * ThinkPad EC register 0x2f (HFSP): fan control loop mode * Supported on almost all ThinkPads @@ -8025,6 +7913,7 @@ static struct ibm_struct volume_driver_data = { #define FAN_NS_CTRL_STATUS BIT(2) /* Bit which determines control is enabled or not */ #define FAN_NS_CTRL BIT(4) /* Bit which determines control is by host or EC */ +#define FAN_CLOCK_TPM (22500*60) /* Ticks per minute for a 22.5 kHz clock */ enum { /* Fan control constants */ fan_status_offset = 0x2f, /* EC register 0x2f */ @@ -8047,6 +7936,7 @@ enum { /* Fan control constants */ enum fan_status_access_mode { TPACPI_FAN_NONE = 0, /* No fan status or control */ TPACPI_FAN_RD_ACPI_GFAN, /* Use ACPI GFAN */ + TPACPI_FAN_RD_ACPI_FANG, /* Use ACPI FANG */ TPACPI_FAN_RD_TPEC, /* Use ACPI EC regs 0x2f, 0x84-0x85 */ TPACPI_FAN_RD_TPEC_NS, /* Use non-standard ACPI EC regs (eg: L13 Yoga gen2 etc.) */ }; @@ -8054,6 +7944,7 @@ enum fan_status_access_mode { enum fan_control_access_mode { TPACPI_FAN_WR_NONE = 0, /* No fan control */ TPACPI_FAN_WR_ACPI_SFAN, /* Use ACPI SFAN */ + TPACPI_FAN_WR_ACPI_FANW, /* Use ACPI FANW */ TPACPI_FAN_WR_TPEC, /* Use ACPI EC reg 0x2f */ TPACPI_FAN_WR_ACPI_FANS, /* Use ACPI FANS and EC reg 0x2f */ }; @@ -8077,6 +7968,8 @@ static u8 fan_control_resume_level; static int fan_watchdog_maxinterval; static bool fan_with_ns_addr; +static bool ecfw_with_fan_dec_rpm; +static bool fan_speed_in_tpr; static struct mutex fan_mutex; @@ -8087,9 +7980,13 @@ TPACPI_HANDLE(fans, ec, "FANS"); /* X31, X40, X41 */ TPACPI_HANDLE(gfan, ec, "GFAN", /* 570 */ "\\FSPD", /* 600e/x, 770e, 770x */ ); /* all others */ +TPACPI_HANDLE(fang, ec, "FANG", /* E531 */ + ); /* all others */ TPACPI_HANDLE(sfan, ec, "SFAN", /* 570 */ "JFNS", /* 770x-JL */ ); /* all others */ +TPACPI_HANDLE(fanw, ec, "FANW", /* E531 */ + ); /* all others */ /* * Unitialized HFSP quirk: ACPI DSDT and EC fail to initialize the @@ -8196,6 +8093,23 @@ static int fan_get_status(u8 *status) break; } + case TPACPI_FAN_RD_ACPI_FANG: { + /* E531 */ + int mode, speed; + + if (unlikely(!acpi_evalf(fang_handle, &mode, NULL, "dd", 0x8100))) + return -EIO; + if (unlikely(!acpi_evalf(fang_handle, &speed, NULL, "dd", 0x8102))) + return -EIO; + + if (likely(status)) { + *status = speed * 7 / 100; + if (mode < 9) + *status |= TP_EC_FAN_AUTO; + } + + break; + } case TPACPI_FAN_RD_TPEC: /* all except 570, 600e/x, 770e, 770x */ if (unlikely(!acpi_ec_read(fan_status_offset, &s))) @@ -8258,8 +8172,11 @@ static int fan_get_speed(unsigned int *speed) !acpi_ec_read(fan_rpm_offset + 1, &hi))) return -EIO; - if (likely(speed)) + if (likely(speed)) { *speed = (hi << 8) | lo; + if (fan_speed_in_tpr && *speed != 0) + *speed = FAN_CLOCK_TPM / *speed; + } break; case TPACPI_FAN_RD_TPEC_NS: if (!acpi_ec_read(fan_rpm_status_ns, &lo)) @@ -8292,8 +8209,11 @@ static int fan2_get_speed(unsigned int *speed) if (rc) return -EIO; - if (likely(speed)) + if (likely(speed)) { *speed = (hi << 8) | lo; + if (fan_speed_in_tpr && *speed != 0) + *speed = FAN_CLOCK_TPM / *speed; + } break; case TPACPI_FAN_RD_TPEC_NS: @@ -8310,6 +8230,17 @@ static int fan2_get_speed(unsigned int *speed) if (speed) *speed = lo ? FAN_RPM_CAL_CONST / lo : 0; break; + case TPACPI_FAN_RD_ACPI_FANG: { + /* E531 */ + int speed_tmp; + + if (unlikely(!acpi_evalf(fang_handle, &speed_tmp, NULL, "dd", 0x8102))) + return -EIO; + + if (likely(speed)) + *speed = speed_tmp * 65535 / 100; + break; + } default: return -ENXIO; @@ -8369,6 +8300,32 @@ static int fan_set_level(int level) tp_features.fan_ctrl_status_undef = 0; break; + case TPACPI_FAN_WR_ACPI_FANW: + if (!(level & TP_EC_FAN_AUTO) && (level < 0 || level > 7)) + return -EINVAL; + if (level & TP_EC_FAN_FULLSPEED) + return -EINVAL; + + if (level & TP_EC_FAN_AUTO) { + if (!acpi_evalf(fanw_handle, NULL, NULL, "vdd", 0x8106, 0x05)) { + return -EIO; + } + if (!acpi_evalf(fanw_handle, NULL, NULL, "vdd", 0x8100, 0x00)) { + return -EIO; + } + } else { + if (!acpi_evalf(fanw_handle, NULL, NULL, "vdd", 0x8106, 0x45)) { + return -EIO; + } + if (!acpi_evalf(fanw_handle, NULL, NULL, "vdd", 0x8100, 0xff)) { + return -EIO; + } + if (!acpi_evalf(fanw_handle, NULL, NULL, "vdd", 0x8102, level * 100 / 7)) { + return -EIO; + } + } + break; + default: return -ENXIO; } @@ -8401,7 +8358,7 @@ static int fan_set_level_safe(int level) static int fan_set_enable(void) { - u8 s; + u8 s = 0; int rc; if (!fan_control_allowed) @@ -8447,6 +8404,19 @@ static int fan_set_enable(void) rc = 0; break; + case TPACPI_FAN_WR_ACPI_FANW: + if (!acpi_evalf(fanw_handle, NULL, NULL, "vdd", 0x8106, 0x05)) { + rc = -EIO; + break; + } + if (!acpi_evalf(fanw_handle, NULL, NULL, "vdd", 0x8100, 0x00)) { + rc = -EIO; + break; + } + + rc = 0; + break; + default: rc = -ENXIO; } @@ -8489,6 +8459,22 @@ static int fan_set_disable(void) fan_control_desired_level = 0; break; + case TPACPI_FAN_WR_ACPI_FANW: + if (!acpi_evalf(fanw_handle, NULL, NULL, "vdd", 0x8106, 0x45)) { + rc = -EIO; + break; + } + if (!acpi_evalf(fanw_handle, NULL, NULL, "vdd", 0x8100, 0xff)) { + rc = -EIO; + break; + } + if (!acpi_evalf(fanw_handle, NULL, NULL, "vdd", 0x8102, 0x00)) { + rc = -EIO; + break; + } + rc = 0; + break; + default: rc = -ENXIO; } @@ -8522,6 +8508,23 @@ static int fan_set_speed(int speed) rc = -EINVAL; break; + case TPACPI_FAN_WR_ACPI_FANW: + if (speed >= 0 && speed <= 65535) { + if (!acpi_evalf(fanw_handle, NULL, NULL, "vdd", 0x8106, 0x45)) { + rc = -EIO; + break; + } + if (!acpi_evalf(fanw_handle, NULL, NULL, "vdd", 0x8100, 0xff)) { + rc = -EIO; + break; + } + if (!acpi_evalf(fanw_handle, NULL, NULL, "vdd", + 0x8102, speed * 100 / 65535)) + rc = -EIO; + } else + rc = -EINVAL; + break; + default: rc = -ENXIO; } @@ -8538,7 +8541,7 @@ static void fan_watchdog_reset(void) if (fan_watchdog_maxinterval > 0 && tpacpi_lifecycle != TPACPI_LIFE_EXITING) mod_delayed_work(tpacpi_wq, &fan_watchdog_task, - msecs_to_jiffies(fan_watchdog_maxinterval * 1000)); + secs_to_jiffies(fan_watchdog_maxinterval)); else cancel_delayed_work(&fan_watchdog_task); } @@ -8719,7 +8722,11 @@ static ssize_t fan_fan1_input_show(struct device *dev, if (res < 0) return res; - return sysfs_emit(buf, "%u\n", speed); + /* Check for fan speeds displayed in hexadecimal */ + if (!ecfw_with_fan_dec_rpm) + return sysfs_emit(buf, "%u\n", speed); + else + return sysfs_emit(buf, "%x\n", speed); } static DEVICE_ATTR(fan1_input, S_IRUGO, fan_fan1_input_show, NULL); @@ -8736,7 +8743,11 @@ static ssize_t fan_fan2_input_show(struct device *dev, if (res < 0) return res; - return sysfs_emit(buf, "%u\n", speed); + /* Check for fan speeds displayed in hexadecimal */ + if (!ecfw_with_fan_dec_rpm) + return sysfs_emit(buf, "%u\n", speed); + else + return sysfs_emit(buf, "%x\n", speed); } static DEVICE_ATTR(fan2_input, S_IRUGO, fan_fan2_input_show, NULL); @@ -8812,6 +8823,9 @@ static const struct attribute_group fan_driver_attr_group = { #define TPACPI_FAN_2CTL 0x0004 /* selects fan2 control */ #define TPACPI_FAN_NOFAN 0x0008 /* no fan available */ #define TPACPI_FAN_NS 0x0010 /* For EC with non-Standard register addresses */ +#define TPACPI_FAN_DECRPM 0x0020 /* For ECFW's with RPM in register as decimal */ +#define TPACPI_FAN_TPR 0x0040 /* Fan speed is in Ticks Per Revolution */ +#define TPACPI_FAN_NOACPI 0x0080 /* Don't use ACPI methods even if detected */ static const struct tpacpi_quirk fan_quirk_table[] __initconst = { TPACPI_QEC_IBM('1', 'Y', TPACPI_FAN_Q1), @@ -8840,6 +8854,11 @@ static const struct tpacpi_quirk fan_quirk_table[] __initconst = { TPACPI_Q_LNV3('R', '1', 'D', TPACPI_FAN_NS), /* 11e Gen5 GL-R */ TPACPI_Q_LNV3('R', '0', 'V', TPACPI_FAN_NS), /* 11e Gen5 KL-Y */ TPACPI_Q_LNV3('N', '1', 'O', TPACPI_FAN_NOFAN), /* X1 Tablet (2nd gen) */ + TPACPI_Q_LNV3('R', '0', 'Q', TPACPI_FAN_DECRPM),/* L480 */ + TPACPI_Q_LNV('8', 'F', TPACPI_FAN_TPR), /* ThinkPad x120e */ + TPACPI_Q_LNV3('R', '0', '0', TPACPI_FAN_NOACPI),/* E560 */ + TPACPI_Q_LNV3('R', '1', '2', TPACPI_FAN_NOACPI),/* T495 */ + TPACPI_Q_LNV3('R', '1', '3', TPACPI_FAN_NOACPI),/* T495s */ }; static int __init fan_init(struct ibm_init_struct *iibm) @@ -8864,6 +8883,10 @@ static int __init fan_init(struct ibm_init_struct *iibm) TPACPI_ACPIHANDLE_INIT(gfan); TPACPI_ACPIHANDLE_INIT(sfan); } + if (tpacpi_is_lenovo()) { + TPACPI_ACPIHANDLE_INIT(fang); + TPACPI_ACPIHANDLE_INIT(fanw); + } quirks = tpacpi_check_quirks(fan_quirk_table, ARRAY_SIZE(fan_quirk_table)); @@ -8880,9 +8903,26 @@ static int __init fan_init(struct ibm_init_struct *iibm) tp_features.fan_ctrl_status_undef = 1; } + /* Check for the EC/BIOS with RPM reported in decimal*/ + if (quirks & TPACPI_FAN_DECRPM) { + pr_info("ECFW with fan RPM as decimal in EC register\n"); + ecfw_with_fan_dec_rpm = 1; + tp_features.fan_ctrl_status_undef = 1; + } + + if (quirks & TPACPI_FAN_NOACPI) { + /* E560, T495, T495s */ + pr_info("Ignoring buggy ACPI fan access method\n"); + fang_handle = NULL; + fanw_handle = NULL; + } + if (gfan_handle) { /* 570, 600e/x, 770e, 770x */ fan_status_access_mode = TPACPI_FAN_RD_ACPI_GFAN; + } else if (fang_handle) { + /* E531 */ + fan_status_access_mode = TPACPI_FAN_RD_ACPI_FANG; } else { /* all other ThinkPads: note that even old-style * ThinkPad ECs supports the fan control register */ @@ -8896,6 +8936,8 @@ static int __init fan_init(struct ibm_init_struct *iibm) if (quirks & TPACPI_FAN_Q1) fan_quirk1_setup(); + if (quirks & TPACPI_FAN_TPR) + fan_speed_in_tpr = true; /* Try and probe the 2nd fan */ tp_features.second_fan = 1; /* needed for get_speed to work */ res = fan2_get_speed(&speed); @@ -8929,6 +8971,11 @@ static int __init fan_init(struct ibm_init_struct *iibm) fan_control_access_mode = TPACPI_FAN_WR_ACPI_SFAN; fan_control_commands |= TPACPI_FAN_CMD_LEVEL | TPACPI_FAN_CMD_ENABLE; + } else if (fanw_handle) { + /* E531 */ + fan_control_access_mode = TPACPI_FAN_WR_ACPI_FANW; + fan_control_commands |= + TPACPI_FAN_CMD_LEVEL | TPACPI_FAN_CMD_SPEED | TPACPI_FAN_CMD_ENABLE; } else { if (!gfan_handle) { /* gfan without sfan means no fan control */ @@ -9080,6 +9127,7 @@ static int fan_read(struct seq_file *m) case TPACPI_FAN_RD_TPEC_NS: case TPACPI_FAN_RD_TPEC: + case TPACPI_FAN_RD_ACPI_FANG: /* all except 570, 600e/x, 770e, 770x */ rc = fan_get_status_safe(&status); if (rc) @@ -9091,7 +9139,11 @@ static int fan_read(struct seq_file *m) if (rc < 0) return rc; - seq_printf(m, "speed:\t\t%d\n", speed); + /* Check for fan speeds displayed in hexadecimal */ + if (!ecfw_with_fan_dec_rpm) + seq_printf(m, "speed:\t\t%d\n", speed); + else + seq_printf(m, "speed:\t\t%x\n", speed); if (fan_status_access_mode == TPACPI_FAN_RD_TPEC_NS) { /* @@ -9100,7 +9152,7 @@ static int fan_read(struct seq_file *m) * No other levels settings available */ seq_printf(m, "level:\t\t%s\n", status & FAN_NS_CTRL ? "unknown" : "auto"); - } else { + } else if (fan_status_access_mode == TPACPI_FAN_RD_TPEC) { if (status & TP_EC_FAN_FULLSPEED) /* Disengaged mode takes precedence */ seq_printf(m, "level:\t\tdisengaged\n"); @@ -9789,7 +9841,7 @@ static ssize_t tpacpi_battery_show(int what, battery = BAT_PRIMARY; if (tpacpi_battery_get(what, battery, &ret)) return -ENODEV; - return sprintf(buf, "%d\n", ret); + return sysfs_emit(buf, "%d\n", ret); } static ssize_t charge_control_start_threshold_show(struct device *device, @@ -9959,6 +10011,7 @@ static const struct tpacpi_quirk battery_quirk_table[] __initconst = { * Individual addressing is broken on models that expose the * primary battery as BAT1. */ + TPACPI_Q_LNV('G', '8', true), /* ThinkPad X131e */ TPACPI_Q_LNV('8', 'F', true), /* Thinkpad X120e */ TPACPI_Q_LNV('J', '7', true), /* B5400 */ TPACPI_Q_LNV('J', 'I', true), /* Thinkpad 11e */ @@ -10318,6 +10371,10 @@ static struct ibm_struct proxsensor_driver_data = { #define DYTC_MODE_PSC_BALANCE 5 /* Default mode aka balanced */ #define DYTC_MODE_PSC_PERFORM 7 /* High power mode aka performance */ +#define DYTC_MODE_PSCV9_LOWPOWER 1 /* Low power mode */ +#define DYTC_MODE_PSCV9_BALANCE 3 /* Default mode aka balanced */ +#define DYTC_MODE_PSCV9_PERFORM 4 /* High power mode aka performance */ + #define DYTC_ERR_MASK 0xF /* Bits 0-3 in cmd result are the error result */ #define DYTC_ERR_SUCCESS 1 /* CMD completed successful */ @@ -10338,6 +10395,10 @@ static int dytc_capabilities; static bool dytc_mmc_get_available; static int profile_force; +static int platform_psc_profile_lowpower = DYTC_MODE_PSC_LOWPOWER; +static int platform_psc_profile_balanced = DYTC_MODE_PSC_BALANCE; +static int platform_psc_profile_performance = DYTC_MODE_PSC_PERFORM; + static int convert_dytc_to_profile(int funcmode, int dytcmode, enum platform_profile_option *profile) { @@ -10359,19 +10420,15 @@ static int convert_dytc_to_profile(int funcmode, int dytcmode, } return 0; case DYTC_FUNCTION_PSC: - switch (dytcmode) { - case DYTC_MODE_PSC_LOWPOWER: + if (dytcmode == platform_psc_profile_lowpower) *profile = PLATFORM_PROFILE_LOW_POWER; - break; - case DYTC_MODE_PSC_BALANCE: + else if (dytcmode == platform_psc_profile_balanced) *profile = PLATFORM_PROFILE_BALANCED; - break; - case DYTC_MODE_PSC_PERFORM: + else if (dytcmode == platform_psc_profile_performance) *profile = PLATFORM_PROFILE_PERFORMANCE; - break; - default: /* Unknown mode */ + else return -EINVAL; - } + return 0; case DYTC_FUNCTION_AMT: /* For now return balanced. It's the closest we have to 'auto' */ @@ -10392,19 +10449,19 @@ static int convert_profile_to_dytc(enum platform_profile_option profile, int *pe if (dytc_capabilities & BIT(DYTC_FC_MMC)) *perfmode = DYTC_MODE_MMC_LOWPOWER; else if (dytc_capabilities & BIT(DYTC_FC_PSC)) - *perfmode = DYTC_MODE_PSC_LOWPOWER; + *perfmode = platform_psc_profile_lowpower; break; case PLATFORM_PROFILE_BALANCED: if (dytc_capabilities & BIT(DYTC_FC_MMC)) *perfmode = DYTC_MODE_MMC_BALANCE; else if (dytc_capabilities & BIT(DYTC_FC_PSC)) - *perfmode = DYTC_MODE_PSC_BALANCE; + *perfmode = platform_psc_profile_balanced; break; case PLATFORM_PROFILE_PERFORMANCE: if (dytc_capabilities & BIT(DYTC_FC_MMC)) *perfmode = DYTC_MODE_MMC_PERFORM; else if (dytc_capabilities & BIT(DYTC_FC_PSC)) - *perfmode = DYTC_MODE_PSC_PERFORM; + *perfmode = platform_psc_profile_performance; break; default: /* Unknown profile */ return -EOPNOTSUPP; @@ -10416,7 +10473,7 @@ static int convert_profile_to_dytc(enum platform_profile_option profile, int *pe * dytc_profile_get: Function to register with platform_profile * handler. Returns current platform profile. */ -static int dytc_profile_get(struct platform_profile_handler *pprof, +static int dytc_profile_get(struct device *dev, enum platform_profile_option *profile) { *profile = dytc_current_profile; @@ -10491,7 +10548,7 @@ static int dytc_cql_command(int command, int *output) * dytc_profile_set: Function to register with platform_profile * handler. Sets current platform profile. */ -static int dytc_profile_set(struct platform_profile_handler *pprof, +static int dytc_profile_set(struct device *dev, enum platform_profile_option profile) { int perfmode; @@ -10540,6 +10597,21 @@ unlock: return err; } +static int dytc_profile_probe(void *drvdata, unsigned long *choices) +{ + set_bit(PLATFORM_PROFILE_LOW_POWER, choices); + set_bit(PLATFORM_PROFILE_BALANCED, choices); + set_bit(PLATFORM_PROFILE_PERFORMANCE, choices); + + return 0; +} + +static const struct platform_profile_ops dytc_profile_ops = { + .probe = dytc_profile_probe, + .profile_get = dytc_profile_get, + .profile_set = dytc_profile_set, +}; + static void dytc_profile_refresh(void) { enum platform_profile_option profile; @@ -10568,24 +10640,14 @@ static void dytc_profile_refresh(void) err = convert_dytc_to_profile(funcmode, perfmode, &profile); if (!err && profile != dytc_current_profile) { dytc_current_profile = profile; - platform_profile_notify(); + platform_profile_notify(tpacpi_pprof); } } -static struct platform_profile_handler dytc_profile = { - .profile_get = dytc_profile_get, - .profile_set = dytc_profile_set, -}; - static int tpacpi_dytc_profile_init(struct ibm_init_struct *iibm) { int err, output; - /* Setup supported modes */ - set_bit(PLATFORM_PROFILE_LOW_POWER, dytc_profile.choices); - set_bit(PLATFORM_PROFILE_BALANCED, dytc_profile.choices); - set_bit(PLATFORM_PROFILE_PERFORMANCE, dytc_profile.choices); - err = dytc_command(DYTC_CMD_QUERY, &output); if (err) return err; @@ -10593,6 +10655,7 @@ static int tpacpi_dytc_profile_init(struct ibm_init_struct *iibm) if (output & BIT(DYTC_QUERY_ENABLE_BIT)) dytc_version = (output >> DYTC_QUERY_REV_BIT) & 0xF; + dbg_printk(TPACPI_DBG_INIT, "DYTC version %d\n", dytc_version); /* Check DYTC is enabled and supports mode setting */ if (dytc_version < 5) return -ENODEV; @@ -10631,6 +10694,11 @@ static int tpacpi_dytc_profile_init(struct ibm_init_struct *iibm) } } else if (dytc_capabilities & BIT(DYTC_FC_PSC)) { /* PSC MODE */ pr_debug("PSC is supported\n"); + if (dytc_version >= 9) { /* update profiles for DYTC 9 and up */ + platform_psc_profile_lowpower = DYTC_MODE_PSCV9_LOWPOWER; + platform_psc_profile_balanced = DYTC_MODE_PSCV9_BALANCE; + platform_psc_profile_performance = DYTC_MODE_PSCV9_PERFORM; + } } else { dbg_printk(TPACPI_DBG_INIT, "No DYTC support available\n"); return -ENODEV; @@ -10640,12 +10708,13 @@ static int tpacpi_dytc_profile_init(struct ibm_init_struct *iibm) "DYTC version %d: thermal mode available\n", dytc_version); /* Create platform_profile structure and register */ - err = platform_profile_register(&dytc_profile); + tpacpi_pprof = platform_profile_register(&tpacpi_pdev->dev, "thinkpad-acpi-profile", + NULL, &dytc_profile_ops); /* * If for some reason platform_profiles aren't enabled * don't quit terminally. */ - if (err) + if (IS_ERR(tpacpi_pprof)) return -ENODEV; /* Ensure initial values are correct */ @@ -10660,7 +10729,8 @@ static int tpacpi_dytc_profile_init(struct ibm_init_struct *iibm) static void dytc_profile_exit(void) { - platform_profile_remove(); + if (!IS_ERR_OR_NULL(tpacpi_pprof)) + platform_profile_remove(tpacpi_pprof); } static struct ibm_struct dytc_profile_driver_data = { @@ -10991,13 +11061,7 @@ static struct ibm_struct auxmac_data = { .name = "auxmac", }; -static ssize_t auxmac_show(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - return sysfs_emit(buf, "%s\n", auxmac); -} -static DEVICE_ATTR_RO(auxmac); +static DEVICE_STRING_ATTR_RO(auxmac, 0444, auxmac); static umode_t auxmac_attr_is_visible(struct kobject *kobj, struct attribute *attr, int n) @@ -11006,7 +11070,7 @@ static umode_t auxmac_attr_is_visible(struct kobject *kobj, } static struct attribute *auxmac_attributes[] = { - &dev_attr_auxmac.attr, + &dev_attr_auxmac.attr.attr, NULL }; @@ -11126,92 +11190,109 @@ static struct platform_driver tpacpi_hwmon_pdriver = { * HKEY event callout for other subdrivers go here * (yes, it is ugly, but it is quick, safe, and gets the job done */ -static void tpacpi_driver_event(const unsigned int hkey_event) +static bool tpacpi_driver_event(const unsigned int hkey_event) { - if (ibm_backlight_device) { - switch (hkey_event) { - case TP_HKEY_EV_BRGHT_UP: - case TP_HKEY_EV_BRGHT_DOWN: + int camera_shutter_state; + + switch (hkey_event) { + case TP_HKEY_EV_BRGHT_UP: + case TP_HKEY_EV_BRGHT_DOWN: + if (ibm_backlight_device) tpacpi_brightness_notify_change(); - } - } - if (alsa_card) { - switch (hkey_event) { - case TP_HKEY_EV_VOL_UP: - case TP_HKEY_EV_VOL_DOWN: - case TP_HKEY_EV_VOL_MUTE: + /* + * Key press events are suppressed by default hotkey_user_mask + * and should still be reported if explicitly requested. + */ + return false; + case TP_HKEY_EV_VOL_UP: + case TP_HKEY_EV_VOL_DOWN: + case TP_HKEY_EV_VOL_MUTE: + if (alsa_card) volume_alsa_notify_change(); - } - } - if (tp_features.kbdlight && hkey_event == TP_HKEY_EV_KBD_LIGHT) { - enum led_brightness brightness; - mutex_lock(&kbdlight_mutex); + /* Key events are suppressed by default hotkey_user_mask */ + return false; + case TP_HKEY_EV_KBD_LIGHT: + if (tp_features.kbdlight) { + enum led_brightness brightness; - /* - * Check the brightness actually changed, setting the brightness - * through kbdlight_set_level() also triggers this event. - */ - brightness = kbdlight_sysfs_get(NULL); - if (kbdlight_brightness != brightness) { - kbdlight_brightness = brightness; - led_classdev_notify_brightness_hw_changed( - &tpacpi_led_kbdlight.led_classdev, brightness); - } + mutex_lock(&kbdlight_mutex); - mutex_unlock(&kbdlight_mutex); - } + /* + * Check the brightness actually changed, setting the brightness + * through kbdlight_set_level() also triggers this event. + */ + brightness = kbdlight_sysfs_get(NULL); + if (kbdlight_brightness != brightness) { + kbdlight_brightness = brightness; + led_classdev_notify_brightness_hw_changed( + &tpacpi_led_kbdlight.led_classdev, brightness); + } - if (hkey_event == TP_HKEY_EV_THM_CSM_COMPLETED) { + mutex_unlock(&kbdlight_mutex); + } + /* Key events are suppressed by default hotkey_user_mask */ + return false; + case TP_HKEY_EV_DFR_CHANGE_ROW: + adaptive_keyboard_change_row(); + return true; + case TP_HKEY_EV_DFR_S_QUICKVIEW_ROW: + adaptive_keyboard_s_quickview_row(); + return true; + case TP_HKEY_EV_THM_CSM_COMPLETED: lapsensor_refresh(); /* If we are already accessing DYTC then skip dytc update */ if (!atomic_add_unless(&dytc_ignore_event, -1, 0)) dytc_profile_refresh(); - } - - if (lcdshadow_dev && hkey_event == TP_HKEY_EV_PRIVACYGUARD_TOGGLE) { - enum drm_privacy_screen_status old_hw_state; - bool changed; - mutex_lock(&lcdshadow_dev->lock); - old_hw_state = lcdshadow_dev->hw_state; - lcdshadow_get_hw_state(lcdshadow_dev); - changed = lcdshadow_dev->hw_state != old_hw_state; - mutex_unlock(&lcdshadow_dev->lock); - - if (changed) - drm_privacy_screen_call_notifier_chain(lcdshadow_dev); - } - if (hkey_event == TP_HKEY_EV_AMT_TOGGLE) { + return true; + case TP_HKEY_EV_PRIVACYGUARD_TOGGLE: + if (lcdshadow_dev) { + enum drm_privacy_screen_status old_hw_state; + bool changed; + + mutex_lock(&lcdshadow_dev->lock); + old_hw_state = lcdshadow_dev->hw_state; + lcdshadow_get_hw_state(lcdshadow_dev); + changed = lcdshadow_dev->hw_state != old_hw_state; + mutex_unlock(&lcdshadow_dev->lock); + + if (changed) + drm_privacy_screen_call_notifier_chain(lcdshadow_dev); + } + return true; + case TP_HKEY_EV_AMT_TOGGLE: /* If we're enabling AMT we need to force balanced mode */ if (!dytc_amt_active) /* This will also set AMT mode enabled */ dytc_profile_set(NULL, PLATFORM_PROFILE_BALANCED); else dytc_control_amt(!dytc_amt_active); - } - if (hkey_event == TP_HKEY_EV_PROFILE_TOGGLE) { - switch (dytc_current_profile) { - case PLATFORM_PROFILE_LOW_POWER: - dytc_profile_set(NULL, PLATFORM_PROFILE_BALANCED); - break; - case PLATFORM_PROFILE_BALANCED: - dytc_profile_set(NULL, PLATFORM_PROFILE_PERFORMANCE); - break; - case PLATFORM_PROFILE_PERFORMANCE: - dytc_profile_set(NULL, PLATFORM_PROFILE_LOW_POWER); - break; - default: - pr_warn("Profile HKEY unexpected profile %d", dytc_current_profile); + + return true; + case TP_HKEY_EV_CAMERASHUTTER_TOGGLE: + camera_shutter_state = get_camera_shutter(); + if (camera_shutter_state < 0) { + pr_err("Error retrieving camera shutter state after shutter event\n"); + return true; } - /* Notify user space the profile changed */ - platform_profile_notify(); + mutex_lock(&tpacpi_inputdev_send_mutex); + + input_report_switch(tpacpi_inputdev, SW_CAMERA_LENS_COVER, camera_shutter_state); + input_sync(tpacpi_inputdev); + + mutex_unlock(&tpacpi_inputdev_send_mutex); + return true; + case TP_HKEY_EV_DOUBLETAP_TOGGLE: + tp_features.trackpoint_doubletap = !tp_features.trackpoint_doubletap; + return true; + case TP_HKEY_EV_PROFILE_TOGGLE: + case TP_HKEY_EV_PROFILE_TOGGLE2: + platform_profile_cycle(); + return true; } -} -static void hotkey_driver_event(const unsigned int scancode) -{ - tpacpi_driver_event(TP_HKEY_EV_HOTKEY_BASE + scancode); + return false; } /* --------------------------------------------------------------------- */ @@ -11443,6 +11524,8 @@ static int __must_check __init get_thinkpad_model_data( tp->vendor = PCI_VENDOR_ID_IBM; else if (dmi_name_in_vendors("LENOVO")) tp->vendor = PCI_VENDOR_ID_LENOVO; + else if (dmi_name_in_vendors("NEC")) + tp->vendor = PCI_VENDOR_ID_LENOVO; else return 0; @@ -11686,7 +11769,7 @@ static int __init set_ibm_param(const char *val, const struct kernel_param *kp) if (strcmp(ibm->name, kp->name) == 0 && ibm->write) { if (strlen(val) > sizeof(ibms_init[i].param) - 1) return -ENOSPC; - strcpy(ibms_init[i].param, val); + strscpy(ibms_init[i].param, val); return 0; } } @@ -11790,37 +11873,18 @@ MODULE_PARM_DESC(profile_force, "Force profile mode. -1=off, 1=MMC, 2=PSC"); static void thinkpad_acpi_module_exit(void) { - struct ibm_struct *ibm, *itmp; - tpacpi_lifecycle = TPACPI_LIFE_EXITING; - if (tpacpi_hwmon) - hwmon_device_unregister(tpacpi_hwmon); - if (tp_features.sensors_pdrv_registered) + if (tpacpi_sensors_pdev) { platform_driver_unregister(&tpacpi_hwmon_pdriver); - if (tp_features.platform_drv_registered) - platform_driver_unregister(&tpacpi_pdriver); - - list_for_each_entry_safe_reverse(ibm, itmp, - &tpacpi_all_drivers, - all_drivers) { - ibm_exit(ibm); - } - - dbg_printk(TPACPI_DBG_INIT, "finished subdriver exit path...\n"); - - if (tpacpi_inputdev) { - if (tp_features.input_device_registered) - input_unregister_device(tpacpi_inputdev); - else - input_free_device(tpacpi_inputdev); - kfree(hotkey_keycode_map); + platform_device_unregister(tpacpi_sensors_pdev); } - if (tpacpi_sensors_pdev) - platform_device_unregister(tpacpi_sensors_pdev); + if (tp_features.platform_drv_registered) + platform_driver_unregister(&tpacpi_pdriver); if (tpacpi_pdev) platform_device_unregister(tpacpi_pdev); + if (proc_dir) remove_proc_entry(TPACPI_PROC_DIR, acpi_root_dir); if (tpacpi_wq) @@ -11832,11 +11896,75 @@ static void thinkpad_acpi_module_exit(void) kfree(thinkpad_id.nummodel_str); } +static void tpacpi_subdrivers_release(void *data) +{ + struct ibm_struct *ibm, *itmp; + + list_for_each_entry_safe_reverse(ibm, itmp, &tpacpi_all_drivers, all_drivers) + ibm_exit(ibm); + + dbg_printk(TPACPI_DBG_INIT, "finished subdriver exit path...\n"); +} + +static int __init tpacpi_pdriver_probe(struct platform_device *pdev) +{ + int ret; + + ret = devm_mutex_init(&pdev->dev, &tpacpi_inputdev_send_mutex); + if (ret) + return ret; + + tpacpi_inputdev = devm_input_allocate_device(&pdev->dev); + if (!tpacpi_inputdev) + return -ENOMEM; + + 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; + 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 (unsigned int i = 0; i < ARRAY_SIZE(ibms_init); i++) { + ret = ibm_init(&ibms_init[i]); + if (ret >= 0 && *ibms_init[i].param) + ret = ibms_init[i].data->write(ibms_init[i].param); + if (ret < 0) { + tpacpi_subdrivers_release(NULL); + return ret; + } + } + + ret = devm_add_action_or_reset(&pdev->dev, tpacpi_subdrivers_release, NULL); + if (ret) + return ret; + + ret = input_register_device(tpacpi_inputdev); + if (ret < 0) + pr_err("unable to register input device\n"); + + return ret; +} + +static int __init tpacpi_hwmon_pdriver_probe(struct platform_device *pdev) +{ + tpacpi_hwmon = devm_hwmon_device_register_with_groups(&pdev->dev, TPACPI_NAME, + NULL, tpacpi_hwmon_groups); + if (IS_ERR(tpacpi_hwmon)) + pr_err("unable to register hwmon device\n"); + + return PTR_ERR_OR_ZERO(tpacpi_hwmon); +} static int __init thinkpad_acpi_module_init(void) { const struct dmi_system_id *dmi_id; - int ret, i; + int ret; acpi_object_type obj_type; tpacpi_lifecycle = TPACPI_LIFE_INIT; @@ -11897,7 +12025,7 @@ static int __init thinkpad_acpi_module_init(void) /* Device initialization */ tpacpi_pdev = platform_device_register_simple(TPACPI_DRVR_NAME, PLATFORM_DEVID_NONE, - NULL, 0); + NULL, 0); if (IS_ERR(tpacpi_pdev)) { ret = PTR_ERR(tpacpi_pdev); tpacpi_pdev = NULL; @@ -11905,50 +12033,8 @@ static int __init thinkpad_acpi_module_init(void) thinkpad_acpi_module_exit(); return ret; } - tpacpi_sensors_pdev = platform_device_register_simple( - TPACPI_HWMON_DRVR_NAME, - PLATFORM_DEVID_NONE, NULL, 0); - if (IS_ERR(tpacpi_sensors_pdev)) { - ret = PTR_ERR(tpacpi_sensors_pdev); - tpacpi_sensors_pdev = NULL; - pr_err("unable to register hwmon platform device\n"); - thinkpad_acpi_module_exit(); - return ret; - } - - mutex_init(&tpacpi_inputdev_send_mutex); - tpacpi_inputdev = input_allocate_device(); - if (!tpacpi_inputdev) { - thinkpad_acpi_module_exit(); - return -ENOMEM; - } else { - /* Prepare input device, but don't register */ - 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; - 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) - ret = ibms_init[i].data->write(ibms_init[i].param); - if (ret < 0) { - thinkpad_acpi_module_exit(); - return ret; - } - } - tpacpi_lifecycle = TPACPI_LIFE_RUNNING; - - ret = platform_driver_register(&tpacpi_pdriver); + ret = platform_driver_probe(&tpacpi_pdriver, tpacpi_pdriver_probe); if (ret) { pr_err("unable to register main platform driver\n"); thinkpad_acpi_module_exit(); @@ -11956,32 +12042,18 @@ static int __init thinkpad_acpi_module_init(void) } tp_features.platform_drv_registered = 1; - ret = platform_driver_register(&tpacpi_hwmon_pdriver); - if (ret) { - pr_err("unable to register hwmon platform driver\n"); - thinkpad_acpi_module_exit(); - return ret; - } - tp_features.sensors_pdrv_registered = 1; - - tpacpi_hwmon = hwmon_device_register_with_groups( - &tpacpi_sensors_pdev->dev, TPACPI_NAME, NULL, tpacpi_hwmon_groups); - if (IS_ERR(tpacpi_hwmon)) { - ret = PTR_ERR(tpacpi_hwmon); - tpacpi_hwmon = NULL; - pr_err("unable to register hwmon device\n"); + tpacpi_sensors_pdev = platform_create_bundle(&tpacpi_hwmon_pdriver, + tpacpi_hwmon_pdriver_probe, + NULL, 0, NULL, 0); + if (IS_ERR(tpacpi_sensors_pdev)) { + ret = PTR_ERR(tpacpi_sensors_pdev); + tpacpi_sensors_pdev = NULL; + pr_err("unable to register hwmon platform device/driver bundle\n"); thinkpad_acpi_module_exit(); return ret; } - ret = input_register_device(tpacpi_inputdev); - if (ret < 0) { - 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; } |