/* * Changelog: * 2007-10-20 changelog trimmed down * * 2007-03-27 0.14 renamed to thinkpad_acpi and moved to * drivers/misc. * * 2006-11-22 0.13 new maintainer * changelog now lives in git commit history, and will * not be updated further in-file. * * 2005-03-17 0.11 support for 600e, 770x * thanks to Jamie Lentin <lentinj@dial.pipex.com> * * 2005-01-16 0.9 use MODULE_VERSION * thanks to Henrik Brix Andersen <brix@gentoo.org> * fix parameter passing on module loading * thanks to Rusty Russell <rusty@rustcorp.com.au> * thanks to Jim Radford <radford@blackbean.org> * 2004-11-08 0.8 fix init error case, don't return from a macro * thanks to Chris Wright <chrisw@osdl.org>
*/
/* 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 */ /* * Thinkpad X1 Tablet series devices emit 0x4012 and 0x4013 * when keyboard cover is attached, detached or folded onto the back
*/
TP_HKEY_EV_KBD_COVER_ATTACH = 0x4012, /* keyboard cover attached */
TP_HKEY_EV_KBD_COVER_DETACH = 0x4013, /* keyboard cover detached or folded back */
#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 staticconstchar *str_supported(int is_supported); #else staticinlineconstchar *str_supported(int is_supported) { return""; } #define vdbg_printk(a_dbg_level, format, arg...) \ do { if (0) no_printk(format, ##arg); } while (0) #endif
staticvoid tpacpi_log_usertask(constchar * const what)
{
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(KERN_DEBUG pr_fmt("%s: PID %d: " format), \
what, task_tgid_vnr(current), ## arg); \
} \
} while (0)
/* * Quirk handling helpers * * ThinkPad IDs and versions seen in the field so far are * two or three characters from the set [0-9A-Z], i.e. base 36. * * We use values well outside that range as specials.
*/
/** * tpacpi_check_quirks() - search BIOS/EC version on a list * @qlist: array of &struct tpacpi_quirk * @qlist_size: number of elements in @qlist * * Iterates over a quirks list until one is found that matches the * ThinkPad's vendor, BIOS and EC model. * * Returns: %0 if nothing matches, otherwise returns the quirks field of * the matching &struct tpacpi_quirk entry. * * The match criteria is: vendor, ec and bios must match.
*/ staticunsignedlong __init tpacpi_check_quirks( conststruct tpacpi_quirk *qlist, unsignedint qlist_size)
{ while (qlist_size) { if ((qlist->vendor == thinkpad_id.vendor ||
qlist->vendor == TPACPI_MATCH_ANY) &&
(qlist->bios == thinkpad_id.bios_model ||
qlist->bios == TPACPI_MATCH_ANY) &&
(qlist->ec == thinkpad_id.ec_model ||
qlist->ec == TPACPI_MATCH_ANY)) return qlist->quirks;
vdbg_printk(TPACPI_DBG_INIT, "trying to locate ACPI handle for %s\n",
name);
for (i = 0; i < num_paths; i++) {
status = acpi_get_handle(parent, paths[i], handle); if (ACPI_SUCCESS(status)) {
dbg_printk(TPACPI_DBG_INIT, "Found ACPI handle %s for %s\n",
paths[i], name); return;
}
}
vdbg_printk(TPACPI_DBG_INIT, "ACPI handle for %s not found\n",
name);
*handle = NULL;
}
staticvoid tpacpi_disable_brightness_delay(void)
{ if (acpi_evalf(hkey_handle, NULL, "PWMS", "qvd", 0))
pr_notice("ACPI backlight control delay disabled\n");
}
staticvoid printk_deprecated_attribute(constchar * const what, constchar * const details)
{
tpacpi_log_usertask("deprecated sysfs attribute");
pr_warn("WARNING: sysfs attribute %s is deprecated and will be removed. %s\n",
what, details);
}
/************************************************************************* * rfkill and radio control support helpers
*/
/* * ThinkPad-ACPI firmware handling model: * * WLSW (master wireless switch) is event-driven, and is common to all * firmware-controlled radios. It cannot be controlled, just monitored, * as expected. It overrides all radio state in firmware * * The kernel, a masked-off hotkey, and WLSW can change the radio state * (TODO: verify how WLSW interacts with the returned radio state). * * The only time there are shadow radio state changes, is when * masked-off hotkeys are used.
*/
/* * Internal driver API for radio state: * * int: < 0 = error, otherwise enum tpacpi_rfkill_state * bool: true means radio blocked (off)
*/ enum tpacpi_rfkill_state {
TPACPI_RFK_RADIO_OFF = 0,
TPACPI_RFK_RADIO_ON
};
/* * Sync the HW-blocking state of all rfkill switches, * do notice it causes the rfkill core to schedule uevents
*/ staticvoid tpacpi_rfk_update_hwblock_state(bool blocked)
{ unsignedint i; struct tpacpi_rfk *tp_rfk;
for (i = 0; i < TPACPI_RFK_SW_MAX; i++) {
tp_rfk = tpacpi_rfkill_switches[i]; if (tp_rfk) { if (rfkill_set_hw_state(tp_rfk->rfkill,
blocked)) { /* ignore -- we track sw block */
}
}
}
}
/* Call to get the WLSW state from the firmware */ staticint hotkey_get_wlsw(void);
/* Call to query WLSW state and update all rfkill switches */ staticbool tpacpi_rfk_check_hwblock_state(void)
{ int res = hotkey_get_wlsw(); int hw_blocked;
/* When unknown or unsupported, we have to assume it is unblocked */ if (res < 0) returnfalse;
atp_rfk = kzalloc(sizeof(struct tpacpi_rfk), GFP_KERNEL); if (atp_rfk)
atp_rfk->rfkill = rfkill_alloc(name,
&tpacpi_pdev->dev,
rfktype,
&tpacpi_rfk_rfkill_ops,
atp_rfk); if (!atp_rfk || !atp_rfk->rfkill) {
pr_err("failed to allocate memory for rfkill class\n");
kfree(atp_rfk); return -ENOMEM;
}
atp_rfk->id = id;
atp_rfk->ops = tp_rfkops;
sw_status = (tp_rfkops->get_status)(); if (sw_status < 0) {
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) { /* try to keep the initial state, since we ask the
* firmware to preserve it across S5 in NVRAM */
rfkill_init_sw_state(atp_rfk->rfkill, sw_state);
}
}
hw_state = tpacpi_rfk_check_hwblock_state();
rfkill_set_hw_state(atp_rfk->rfkill, hw_state);
res = rfkill_register(atp_rfk->rfkill); if (res < 0) {
pr_err("failed to register %s rfkill switch: %d\n", name, res);
rfkill_destroy(atp_rfk->rfkill);
kfree(atp_rfk); return res;
}
tpacpi_rfkill_switches[id] = atp_rfk;
pr_info("rfkill switch %s: radio is %sblocked\n",
name, (sw_state || hw_state) ? "" : "un"); return 0;
}
/* This is in the ABI... */ if (tpacpi_rfk_check_hwblock_state()) {
status = TPACPI_RFK_RADIO_OFF;
} else {
status = tpacpi_rfk_update_swstate(tpacpi_rfkill_switches[id]); if (status < 0) return status;
}
tpacpi_disclose_usertask(attr->attr.name, "set to %ld\n", t);
/* This is in the ABI... */ if (tpacpi_rfk_check_hwblock_state() && !!t) return -EPERM;
res = tpacpi_rfkill_switches[id]->ops->set_status((!!t) ?
TPACPI_RFK_RADIO_ON : TPACPI_RFK_RADIO_OFF);
tpacpi_rfk_update_swstate(tpacpi_rfkill_switches[id]);
return (res < 0) ? res : count;
}
/* procfs -------------------------------------------------------------- */ staticint tpacpi_rfk_procfs_read(constenum tpacpi_rfk_id id, struct seq_file *m)
{ if (id >= TPACPI_RFK_SW_MAX)
seq_printf(m, "status:\t\tnot supported\n"); else { int status;
/* This is in the ABI... */ if (tpacpi_rfk_check_hwblock_state()) {
status = TPACPI_RFK_RADIO_OFF;
} else {
status = tpacpi_rfk_update_swstate(
tpacpi_rfkill_switches[id]); if (status < 0) return status;
}
/************************************************************************* * Firmware Data
*/
/* * Table of recommended minimum BIOS versions * * Reasons for listing: * 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 * * 3. BIOS with known reduced functionality in older versions * * We recommend the latest BIOS and EC version. * We only support the latest BIOS and EC fw version as a rule. * * Sources: IBM ThinkPad Public Web Documents (update changelogs), * Information from users in ThinkWiki * * WARNING: we use this table also to detect that the machine is * a ThinkPad in some cases, so don't remove entries lightly.
*/
/* note that unknown versions are set to 0x0000 and we use that */ if ((bios_version > thinkpad_id.bios_release) ||
(ec_version > thinkpad_id.ec_release &&
ec_version != TPACPI_MATCH_ANY_VERSION)) { /* * The changelogs would let us track down the exact * reason, but it is just too much of a pain to track * it. We only list BIOSes that are either really * broken, or really stable to begin with, so it is * best if the user upgrades the firmware anyway.
*/
pr_warn("WARNING: Outdated ThinkPad BIOS/EC firmware\n");
pr_warn("WARNING: This firmware may be missing critical bug fixes and/or important features\n");
}
}
/* * ThinkPad firmware event model * * The ThinkPad firmware has two main event interfaces: normal ACPI * notifications (which follow the ACPI standard), and a private event * interface. * * The private event interface also issues events for the hotkeys. As * the driver gained features, the event handling code ended up being * built around the hotkey subdriver. This will need to be refactored * to a more formal event API eventually. * * Some "hotkeys" are actually supposed to be used as event reports, * such as "brightness has changed", "volume has changed", depending on * the ThinkPad model and how the firmware is operating. * * Unlike other classes, hotkey-class events have mask/unmask control on * non-ancient firmware. However, how it behaves changes a lot with the * firmware model and version.
*/
/* kthread for the hotkey poller */ staticstruct task_struct *tpacpi_hotkey_task;
/* * Acquire mutex to write poller control variables as an * atomic block. * * Increment hotkey_config_change when changing them if you * want the kthread to forget old state. * * See HOTKEY_CONFIG_CRITICAL_START/HOTKEY_CONFIG_CRITICAL_END
*/ staticstruct mutex hotkey_thread_data_mutex; staticunsignedint hotkey_config_change;
/* * hotkey poller control variables * * Must be atomic or readers will also need to acquire mutex * * HOTKEY_CONFIG_CRITICAL_START/HOTKEY_CONFIG_CRITICAL_END * should be used only when the changes need to be taken as * a block, OR when one needs to force the kthread to forget * old state.
*/ static u32 hotkey_source_mask; /* bit mask 0=ACPI,1=NVRAM */ staticunsignedint hotkey_poll_freq = 10; /* Hz */
#define HOTKEY_CONFIG_CRITICAL_START \ do { \
mutex_lock(&hotkey_thread_data_mutex); \
hotkey_config_change++; \
} while (0); #define HOTKEY_CONFIG_CRITICAL_END \
mutex_unlock(&hotkey_thread_data_mutex);
enum { /* The following modes are considered tablet mode for the purpose of * reporting the status to userspace. i.e. in all these modes it makes * sense to disable the laptop input devices such as touchpad and * keyboard.
*/
TP_ACPI_MULTI_MODE_TABLET_LIKE = TP_ACPI_MULTI_MODE_TABLET |
TP_ACPI_MULTI_MODE_STAND |
TP_ACPI_MULTI_MODE_TENT |
TP_ACPI_MULTI_MODE_STAND_TENT,
};
staticint hotkey_gmms_get_tablet_mode(int s, int *has_tablet_mode)
{ int type = (s >> 16) & 0xffff; int value = s & 0xffff; int mode = TP_ACPI_MULTI_MODE_INVALID; int valid_modes = 0;
if (has_tablet_mode)
*has_tablet_mode = 0;
switch (type) { case 1:
valid_modes = TP_ACPI_MULTI_MODE_LAPTOP |
TP_ACPI_MULTI_MODE_TABLET |
TP_ACPI_MULTI_MODE_STAND_TENT; break; case 2:
valid_modes = TP_ACPI_MULTI_MODE_LAPTOP |
TP_ACPI_MULTI_MODE_FLAT |
TP_ACPI_MULTI_MODE_TABLET |
TP_ACPI_MULTI_MODE_STAND |
TP_ACPI_MULTI_MODE_TENT; break; case 3:
valid_modes = TP_ACPI_MULTI_MODE_LAPTOP |
TP_ACPI_MULTI_MODE_FLAT; break; case 4: case 5: /* In mode 4, FLAT is not specified as a valid mode. However, * it can be seen at least on the X1 Yoga 2nd Generation.
*/
valid_modes = TP_ACPI_MULTI_MODE_LAPTOP |
TP_ACPI_MULTI_MODE_FLAT |
TP_ACPI_MULTI_MODE_TABLET |
TP_ACPI_MULTI_MODE_STAND |
TP_ACPI_MULTI_MODE_TENT; break; default:
pr_err("Unknown multi mode status type %d with value 0x%04X, please report this to %s\n",
type, value, TPACPI_MAIL); return 0;
}
if (has_tablet_mode && (valid_modes & TP_ACPI_MULTI_MODE_TABLET_LIKE))
*has_tablet_mode = 1;
switch (value) { case 1:
mode = TP_ACPI_MULTI_MODE_LAPTOP; break; case 2:
mode = TP_ACPI_MULTI_MODE_FLAT; break; case 3:
mode = TP_ACPI_MULTI_MODE_TABLET; break; case 4: if (type == 1)
mode = TP_ACPI_MULTI_MODE_STAND_TENT; else
mode = TP_ACPI_MULTI_MODE_STAND; break; case 5:
mode = TP_ACPI_MULTI_MODE_TENT; break; default: if (type == 5 && value == 0xffff) {
pr_warn("Multi mode status is undetected, assuming laptop\n"); return 0;
}
}
if (!(mode & valid_modes)) {
pr_err("Unknown/reserved multi mode value 0x%04X for type %d, please report this to %s\n",
value, type, TPACPI_MAIL); return 0;
}
/* * Reads current event mask from firmware, and updates * hotkey_acpi_mask accordingly. Also resets any bits * from hotkey_user_mask that are unavailable to be * delivered (shadow requirement of the userspace ABI).
*/ staticint hotkey_mask_get(void)
{
lockdep_assert_held(&hotkey_mutex);
if (tp_features.hotkey_mask) {
u32 m = 0;
if (!acpi_evalf(hkey_handle, &m, "DHKN", "d")) return -EIO;
hotkey_acpi_mask = m;
} else { /* no mask support doesn't mean no event support... */
hotkey_acpi_mask = hotkey_all_mask;
}
staticvoid hotkey_mask_warn_incomplete_mask(void)
{ /* log only what the user can fix... */ const u32 wantedmask = hotkey_driver_mask &
~(hotkey_acpi_mask | hotkey_source_mask) &
(hotkey_all_mask | TPACPI_HKEY_NVRAM_KNOWN_MASK);
if (wantedmask)
pr_notice("required events 0x%08x not enabled!\n", wantedmask);
}
/* * Set the firmware mask when supported * * Also calls hotkey_mask_get to update hotkey_acpi_mask. * * NOTE: does not set bits in hotkey_user_mask, but may reset them.
*/ staticint hotkey_mask_set(u32 mask)
{ int i; int rc = 0;
const u32 fwmask = mask & ~hotkey_source_mask;
lockdep_assert_held(&hotkey_mutex);
if (tp_features.hotkey_mask) { for (i = 0; i < 32; i++) { if (!acpi_evalf(hkey_handle,
NULL, "MHKM", "vdd", i + 1,
!!(mask & (1 << i)))) {
rc = -EIO; break;
}
}
}
/* * We *must* make an inconditional call to hotkey_mask_get to * refresh hotkey_acpi_mask and update hotkey_user_mask * * Take the opportunity to also log when we cannot _enable_ * a given event.
*/ if (!hotkey_mask_get() && !rc && (fwmask & ~hotkey_acpi_mask)) {
pr_notice("asked for hotkey mask 0x%08x, but firmware forced it to 0x%08x\n",
fwmask, hotkey_acpi_mask);
}
if (tpacpi_lifecycle != TPACPI_LIFE_EXITING)
hotkey_mask_warn_incomplete_mask();
return rc;
}
/* * Sets hotkey_user_mask and tries to set the firmware mask
*/ staticint hotkey_user_mask_set(const u32 mask)
{ int rc;
lockdep_assert_held(&hotkey_mutex);
/* Give people a chance to notice they are doing something that
* is bound to go boom on their users sooner or later */ if (!tp_warned.hotkey_mask_ff &&
(mask == 0xffff || mask == 0xffffff ||
mask == 0xffffffff)) {
tp_warned.hotkey_mask_ff = 1;
pr_notice("setting the hotkey mask to 0x%08x is likely not the best way to go about it\n",
mask);
pr_notice("please consider using the driver defaults, and refer to up-to-date thinkpad-acpi documentation\n");
}
/* Try to enable what the user asked for, plus whatever we need.
* this syncs everything but won't enable bits in hotkey_user_mask */
rc = hotkey_mask_set((mask | hotkey_driver_mask) & ~hotkey_source_mask);
/* Enable the available bits in hotkey_user_mask */
hotkey_user_mask = mask & (hotkey_acpi_mask | hotkey_source_mask);
return rc;
}
/* * Sets the driver hotkey mask. * * Can be called even if the hotkey subdriver is inactive
*/ staticint tpacpi_hotkey_driver_mask_set(const u32 mask)
{ int rc;
/* Do the right thing if hotkey_init has not been called yet */ if (!tp_features.hotkey) {
hotkey_driver_mask = mask; return 0;
}
/* * 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))) returntrue; /* Not reported but still a known code */
} elseif (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;
} elseif (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;
/* Do NOT call without validating scancode first */ staticvoid tpacpi_hotkey_send_key(unsignedint scancode)
{
tpacpi_input_send_key(TP_HKEY_EV_ORIG_KEY_START + scancode, NULL);
}
staticvoid hotkey_read_nvram(struct tp_nvram_state *n, const u32 m)
{
u8 d;
if (m & TP_NVRAM_HKEY_GROUP_HK2) {
d = nvram_read_byte(TP_NVRAM_ADDR_HK2);
n->thinkpad_toggle = !!(d & TP_NVRAM_MASK_HKT_THINKPAD);
n->zoom_toggle = !!(d & TP_NVRAM_MASK_HKT_ZOOM);
n->display_toggle = !!(d & TP_NVRAM_MASK_HKT_DISPLAY);
n->hibernate_toggle = !!(d & TP_NVRAM_MASK_HKT_HIBERNATE);
} if (m & TP_ACPI_HKEY_KBD_LIGHT_MASK) {
d = nvram_read_byte(TP_NVRAM_ADDR_THINKLIGHT);
n->thinklight_toggle = !!(d & TP_NVRAM_MASK_THINKLIGHT);
} if (m & TP_ACPI_HKEY_DISPXPAND_MASK) {
d = nvram_read_byte(TP_NVRAM_ADDR_VIDEO);
n->displayexp_toggle =
!!(d & TP_NVRAM_MASK_HKT_DISPEXPND);
} if (m & TP_NVRAM_HKEY_GROUP_BRIGHTNESS) {
d = nvram_read_byte(TP_NVRAM_ADDR_BRIGHTNESS);
--> --------------------
--> maximum size reached
--> --------------------
Messung V0.5
¤ Dauer der Verarbeitung: 0.116 Sekunden
(vorverarbeitet)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.