提交 9c0a76e1 编写于 作者: H Henrique de Moraes Holschuh 提交者: Len Brown

thinkpad-acpi: fix initialization error paths

Rework some subdriver init and exit handlers, in order to fix some
initialization error paths that were missing, or broken.

Hitting those bugs should be extremely rare in the real world, but should
that happen, thinkpad-acpi would fail to dealocate some resources and a
reboot might well be needed to be able to load the driver again.
Signed-off-by: NHenrique de Moraes Holschuh <hmh@hmh.eng.br>
Signed-off-by: NLen Brown <len.brown@intel.com>
上级 197a2cd9
...@@ -1921,6 +1921,29 @@ static struct attribute *hotkey_mask_attributes[] __initdata = { ...@@ -1921,6 +1921,29 @@ static struct attribute *hotkey_mask_attributes[] __initdata = {
&dev_attr_hotkey_wakeup_hotunplug_complete.attr, &dev_attr_hotkey_wakeup_hotunplug_complete.attr,
}; };
static void hotkey_exit(void)
{
#ifdef CONFIG_THINKPAD_ACPI_HOTKEY_POLL
hotkey_poll_stop_sync();
#endif
if (hotkey_dev_attributes)
delete_attr_set(hotkey_dev_attributes, &tpacpi_pdev->dev.kobj);
kfree(hotkey_keycode_map);
if (tp_features.hotkey) {
dbg_printk(TPACPI_DBG_EXIT,
"restoring original hot key mask\n");
/* no short-circuit boolean operator below! */
if ((hotkey_mask_set(hotkey_orig_mask) |
hotkey_status_set(hotkey_orig_status)) != 0)
printk(TPACPI_ERR
"failed to restore hot key mask "
"to BIOS defaults\n");
}
}
static int __init hotkey_init(struct ibm_init_struct *iibm) static int __init hotkey_init(struct ibm_init_struct *iibm)
{ {
/* Requirements for changing the default keymaps: /* Requirements for changing the default keymaps:
...@@ -2060,226 +2083,220 @@ static int __init hotkey_init(struct ibm_init_struct *iibm) ...@@ -2060,226 +2083,220 @@ static int __init hotkey_init(struct ibm_init_struct *iibm)
vdbg_printk(TPACPI_DBG_INIT, "hotkeys are %s\n", vdbg_printk(TPACPI_DBG_INIT, "hotkeys are %s\n",
str_supported(tp_features.hotkey)); str_supported(tp_features.hotkey));
if (tp_features.hotkey) { if (!tp_features.hotkey)
hotkey_dev_attributes = create_attr_set(13, NULL); return 1;
if (!hotkey_dev_attributes)
return -ENOMEM;
res = add_many_to_attr_set(hotkey_dev_attributes,
hotkey_attributes,
ARRAY_SIZE(hotkey_attributes));
if (res)
return res;
/* mask not supported on 570, 600e/x, 770e, 770x, A21e, A2xm/p, hotkey_dev_attributes = create_attr_set(13, NULL);
A30, R30, R31, T20-22, X20-21, X22-24. Detected by checking if (!hotkey_dev_attributes)
for HKEY interface version 0x100 */ return -ENOMEM;
if (acpi_evalf(hkey_handle, &hkeyv, "MHKV", "qd")) { res = add_many_to_attr_set(hotkey_dev_attributes,
if ((hkeyv >> 8) != 1) { hotkey_attributes,
printk(TPACPI_ERR "unknown version of the " ARRAY_SIZE(hotkey_attributes));
"HKEY interface: 0x%x\n", hkeyv); if (res)
printk(TPACPI_ERR "please report this to %s\n", goto err_exit;
TPACPI_MAIL);
} else { /* mask not supported on 570, 600e/x, 770e, 770x, A21e, A2xm/p,
/* A30, R30, R31, T20-22, X20-21, X22-24. Detected by checking
* MHKV 0x100 in A31, R40, R40e, for HKEY interface version 0x100 */
* T4x, X31, and later if (acpi_evalf(hkey_handle, &hkeyv, "MHKV", "qd")) {
*/ if ((hkeyv >> 8) != 1) {
tp_features.hotkey_mask = 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);
} else {
/*
* MHKV 0x100 in A31, R40, R40e,
* T4x, X31, and later
*/
tp_features.hotkey_mask = 1;
} }
}
vdbg_printk(TPACPI_DBG_INIT, "hotkey masks are %s\n", vdbg_printk(TPACPI_DBG_INIT, "hotkey masks are %s\n",
str_supported(tp_features.hotkey_mask)); str_supported(tp_features.hotkey_mask));
if (tp_features.hotkey_mask) { if (tp_features.hotkey_mask) {
if (!acpi_evalf(hkey_handle, &hotkey_all_mask, if (!acpi_evalf(hkey_handle, &hotkey_all_mask,
"MHKA", "qd")) { "MHKA", "qd")) {
printk(TPACPI_ERR printk(TPACPI_ERR
"missing MHKA handler, " "missing MHKA handler, "
"please report this to %s\n", "please report this to %s\n",
TPACPI_MAIL); TPACPI_MAIL);
/* FN+F12, FN+F4, FN+F3 */ /* FN+F12, FN+F4, FN+F3 */
hotkey_all_mask = 0x080cU; hotkey_all_mask = 0x080cU;
}
} }
}
/* hotkey_source_mask *must* be zero for /* hotkey_source_mask *must* be zero for
* the first hotkey_mask_get */ * the first hotkey_mask_get */
res = hotkey_status_get(&hotkey_orig_status); res = hotkey_status_get(&hotkey_orig_status);
if (!res && tp_features.hotkey_mask) { if (res)
res = hotkey_mask_get(); goto err_exit;
hotkey_orig_mask = hotkey_mask;
if (!res) { if (tp_features.hotkey_mask) {
res = add_many_to_attr_set( res = hotkey_mask_get();
hotkey_dev_attributes, if (res)
hotkey_mask_attributes, goto err_exit;
ARRAY_SIZE(hotkey_mask_attributes));
} hotkey_orig_mask = hotkey_mask;
} res = add_many_to_attr_set(
hotkey_dev_attributes,
hotkey_mask_attributes,
ARRAY_SIZE(hotkey_mask_attributes));
if (res)
goto err_exit;
}
#ifdef CONFIG_THINKPAD_ACPI_HOTKEY_POLL #ifdef CONFIG_THINKPAD_ACPI_HOTKEY_POLL
if (tp_features.hotkey_mask) { if (tp_features.hotkey_mask) {
hotkey_source_mask = TPACPI_HKEY_NVRAM_GOOD_MASK hotkey_source_mask = TPACPI_HKEY_NVRAM_GOOD_MASK
& ~hotkey_all_mask; & ~hotkey_all_mask;
} else { } else {
hotkey_source_mask = TPACPI_HKEY_NVRAM_GOOD_MASK; hotkey_source_mask = TPACPI_HKEY_NVRAM_GOOD_MASK;
} }
vdbg_printk(TPACPI_DBG_INIT, vdbg_printk(TPACPI_DBG_INIT,
"hotkey source mask 0x%08x, polling freq %d\n", "hotkey source mask 0x%08x, polling freq %d\n",
hotkey_source_mask, hotkey_poll_freq); hotkey_source_mask, hotkey_poll_freq);
#endif #endif
/* Not all thinkpads have a hardware radio switch */ /* Not all thinkpads have a hardware radio switch */
if (!res && acpi_evalf(hkey_handle, &status, "WLSW", "qd")) { if (acpi_evalf(hkey_handle, &status, "WLSW", "qd")) {
tp_features.hotkey_wlsw = 1; tp_features.hotkey_wlsw = 1;
printk(TPACPI_INFO printk(TPACPI_INFO
"radio switch found; radios are %s\n", "radio switch found; radios are %s\n",
enabled(status, 0)); enabled(status, 0));
res = add_to_attr_set(hotkey_dev_attributes, res = add_to_attr_set(hotkey_dev_attributes,
&dev_attr_hotkey_radio_sw.attr); &dev_attr_hotkey_radio_sw.attr);
} }
/* For X41t, X60t, X61t Tablets... */ /* For X41t, X60t, X61t Tablets... */
if (!res && acpi_evalf(hkey_handle, &status, "MHKG", "qd")) { if (!res && acpi_evalf(hkey_handle, &status, "MHKG", "qd")) {
tp_features.hotkey_tablet = 1; tp_features.hotkey_tablet = 1;
printk(TPACPI_INFO printk(TPACPI_INFO
"possible tablet mode switch found; " "possible tablet mode switch found; "
"ThinkPad in %s mode\n", "ThinkPad in %s mode\n",
(status & TP_HOTKEY_TABLET_MASK)? (status & TP_HOTKEY_TABLET_MASK)?
"tablet" : "laptop"); "tablet" : "laptop");
res = add_to_attr_set(hotkey_dev_attributes, res = add_to_attr_set(hotkey_dev_attributes,
&dev_attr_hotkey_tablet_mode.attr); &dev_attr_hotkey_tablet_mode.attr);
} }
if (!res) if (!res)
res = register_attr_set_with_sysfs( res = register_attr_set_with_sysfs(
hotkey_dev_attributes, hotkey_dev_attributes,
&tpacpi_pdev->dev.kobj); &tpacpi_pdev->dev.kobj);
if (res) if (res)
return res; goto err_exit;
/* Set up key map */ /* Set up key map */
hotkey_keycode_map = kmalloc(TPACPI_HOTKEY_MAP_SIZE, hotkey_keycode_map = kmalloc(TPACPI_HOTKEY_MAP_SIZE,
GFP_KERNEL); GFP_KERNEL);
if (!hotkey_keycode_map) { if (!hotkey_keycode_map) {
printk(TPACPI_ERR printk(TPACPI_ERR
"failed to allocate memory for key map\n"); "failed to allocate memory for key map\n");
return -ENOMEM; res = -ENOMEM;
} goto err_exit;
}
if (thinkpad_id.vendor == PCI_VENDOR_ID_LENOVO) { if (thinkpad_id.vendor == PCI_VENDOR_ID_LENOVO) {
dbg_printk(TPACPI_DBG_INIT, dbg_printk(TPACPI_DBG_INIT,
"using Lenovo default hot key map\n"); "using Lenovo default hot key map\n");
memcpy(hotkey_keycode_map, &lenovo_keycode_map, memcpy(hotkey_keycode_map, &lenovo_keycode_map,
TPACPI_HOTKEY_MAP_SIZE); TPACPI_HOTKEY_MAP_SIZE);
} else {
dbg_printk(TPACPI_DBG_INIT,
"using IBM default hot key map\n");
memcpy(hotkey_keycode_map, &ibm_keycode_map,
TPACPI_HOTKEY_MAP_SIZE);
}
set_bit(EV_KEY, tpacpi_inputdev->evbit);
set_bit(EV_MSC, tpacpi_inputdev->evbit);
set_bit(MSC_SCAN, tpacpi_inputdev->mscbit);
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) {
set_bit(hotkey_keycode_map[i],
tpacpi_inputdev->keybit);
} else { } else {
dbg_printk(TPACPI_DBG_INIT, if (i < sizeof(hotkey_reserved_mask)*8)
"using IBM default hot key map\n"); hotkey_reserved_mask |= 1 << i;
memcpy(hotkey_keycode_map, &ibm_keycode_map,
TPACPI_HOTKEY_MAP_SIZE);
}
set_bit(EV_KEY, tpacpi_inputdev->evbit);
set_bit(EV_MSC, tpacpi_inputdev->evbit);
set_bit(MSC_SCAN, tpacpi_inputdev->mscbit);
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) {
set_bit(hotkey_keycode_map[i],
tpacpi_inputdev->keybit);
} else {
if (i < sizeof(hotkey_reserved_mask)*8)
hotkey_reserved_mask |= 1 << i;
}
}
if (tp_features.hotkey_wlsw) {
set_bit(EV_SW, tpacpi_inputdev->evbit);
set_bit(SW_RFKILL_ALL, tpacpi_inputdev->swbit);
}
if (tp_features.hotkey_tablet) {
set_bit(EV_SW, tpacpi_inputdev->evbit);
set_bit(SW_TABLET_MODE, tpacpi_inputdev->swbit);
} }
}
/* Do not issue duplicate brightness change events to if (tp_features.hotkey_wlsw) {
* userspace */ set_bit(EV_SW, tpacpi_inputdev->evbit);
if (!tp_features.bright_acpimode) set_bit(SW_RFKILL_ALL, tpacpi_inputdev->swbit);
/* update bright_acpimode... */ }
tpacpi_check_std_acpi_brightness_support(); if (tp_features.hotkey_tablet) {
set_bit(EV_SW, tpacpi_inputdev->evbit);
if (tp_features.bright_acpimode) { set_bit(SW_TABLET_MODE, tpacpi_inputdev->swbit);
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");
/* The hotkey_reserved_mask change below is not
* necessary while the keys are at KEY_RESERVED in the
* default map, but better safe than sorry, leave it
* here as a marker of what we have to do, especially
* when we finally become able to set this at runtime
* on response to X.org requests */
hotkey_reserved_mask |=
(1 << TP_ACPI_HOTKEYSCAN_FNHOME)
| (1 << TP_ACPI_HOTKEYSCAN_FNEND);
}
dbg_printk(TPACPI_DBG_INIT, /* Do not issue duplicate brightness change events to
"enabling hot key handling\n"); * userspace */
res = hotkey_status_set(1); if (!tp_features.bright_acpimode)
if (res) /* update bright_acpimode... */
return res; tpacpi_check_std_acpi_brightness_support();
res = hotkey_mask_set(((hotkey_all_mask | hotkey_source_mask)
& ~hotkey_reserved_mask)
| hotkey_orig_mask);
if (res < 0 && res != -ENXIO)
return res;
dbg_printk(TPACPI_DBG_INIT, if (tp_features.bright_acpimode) {
"legacy hot key reporting over procfs %s\n", printk(TPACPI_INFO
(hotkey_report_mode < 2) ? "This ThinkPad has standard ACPI backlight "
"enabled" : "disabled"); "brightness control, supported by the ACPI "
"video driver\n");
printk(TPACPI_NOTICE
"Disabling thinkpad-acpi brightness events "
"by default...\n");
/* The hotkey_reserved_mask change below is not
* necessary while the keys are at KEY_RESERVED in the
* default map, but better safe than sorry, leave it
* here as a marker of what we have to do, especially
* when we finally become able to set this at runtime
* on response to X.org requests */
hotkey_reserved_mask |=
(1 << TP_ACPI_HOTKEYSCAN_FNHOME)
| (1 << TP_ACPI_HOTKEYSCAN_FNEND);
}
dbg_printk(TPACPI_DBG_INIT, "enabling hot key handling\n");
res = hotkey_status_set(1);
if (res) {
hotkey_exit();
return res;
}
res = hotkey_mask_set(((hotkey_all_mask | hotkey_source_mask)
& ~hotkey_reserved_mask)
| hotkey_orig_mask);
if (res < 0 && res != -ENXIO) {
hotkey_exit();
return res;
}
tpacpi_inputdev->open = &hotkey_inputdev_open; dbg_printk(TPACPI_DBG_INIT,
tpacpi_inputdev->close = &hotkey_inputdev_close; "legacy hot key reporting over procfs %s\n",
(hotkey_report_mode < 2) ?
"enabled" : "disabled");
hotkey_poll_setup_safe(1); tpacpi_inputdev->open = &hotkey_inputdev_open;
tpacpi_input_send_radiosw(); tpacpi_inputdev->close = &hotkey_inputdev_close;
tpacpi_input_send_tabletsw();
}
return (tp_features.hotkey)? 0 : 1; hotkey_poll_setup_safe(1);
} tpacpi_input_send_radiosw();
tpacpi_input_send_tabletsw();
static void hotkey_exit(void) return 0;
{
#ifdef CONFIG_THINKPAD_ACPI_HOTKEY_POLL
hotkey_poll_stop_sync();
#endif
if (tp_features.hotkey) { err_exit:
dbg_printk(TPACPI_DBG_EXIT, delete_attr_set(hotkey_dev_attributes, &tpacpi_pdev->dev.kobj);
"restoring original hot key mask\n"); hotkey_dev_attributes = NULL;
/* no short-circuit boolean operator below! */
if ((hotkey_mask_set(hotkey_orig_mask) |
hotkey_status_set(hotkey_orig_status)) != 0)
printk(TPACPI_ERR
"failed to restore hot key mask "
"to BIOS defaults\n");
}
if (hotkey_dev_attributes) { return (res < 0)? res : 1;
delete_attr_set(hotkey_dev_attributes, &tpacpi_pdev->dev.kobj);
hotkey_dev_attributes = NULL;
}
} }
static void hotkey_notify(struct ibm_struct *ibm, u32 event) static void hotkey_notify(struct ibm_struct *ibm, u32 event)
...@@ -3319,7 +3336,7 @@ static struct tpacpi_led_classdev tpacpi_led_thinklight = { ...@@ -3319,7 +3336,7 @@ static struct tpacpi_led_classdev tpacpi_led_thinklight = {
static int __init light_init(struct ibm_init_struct *iibm) static int __init light_init(struct ibm_init_struct *iibm)
{ {
int rc = 0; int rc;
vdbg_printk(TPACPI_DBG_INIT, "initializing light subdriver\n"); vdbg_printk(TPACPI_DBG_INIT, "initializing light subdriver\n");
...@@ -3337,20 +3354,23 @@ static int __init light_init(struct ibm_init_struct *iibm) ...@@ -3337,20 +3354,23 @@ static int __init light_init(struct ibm_init_struct *iibm)
tp_features.light_status = tp_features.light_status =
acpi_evalf(ec_handle, NULL, "KBLT", "qv"); acpi_evalf(ec_handle, NULL, "KBLT", "qv");
vdbg_printk(TPACPI_DBG_INIT, "light is %s\n", vdbg_printk(TPACPI_DBG_INIT, "light is %s, light status is %s\n",
str_supported(tp_features.light)); str_supported(tp_features.light),
str_supported(tp_features.light_status));
if (tp_features.light) { if (!tp_features.light)
rc = led_classdev_register(&tpacpi_pdev->dev, return 1;
&tpacpi_led_thinklight.led_classdev);
} rc = led_classdev_register(&tpacpi_pdev->dev,
&tpacpi_led_thinklight.led_classdev);
if (rc < 0) { if (rc < 0) {
tp_features.light = 0; tp_features.light = 0;
tp_features.light_status = 0; tp_features.light_status = 0;
} else { } else {
rc = (tp_features.light)? 0 : 1; rc = 0;
} }
return rc; return rc;
} }
...@@ -3978,7 +3998,6 @@ static void led_exit(void) ...@@ -3978,7 +3998,6 @@ static void led_exit(void)
} }
kfree(tpacpi_leds); kfree(tpacpi_leds);
tpacpi_leds = NULL;
} }
static int __init led_init(struct ibm_init_struct *iibm) static int __init led_init(struct ibm_init_struct *iibm)
...@@ -4802,7 +4821,6 @@ static void brightness_exit(void) ...@@ -4802,7 +4821,6 @@ static void brightness_exit(void)
vdbg_printk(TPACPI_DBG_EXIT, vdbg_printk(TPACPI_DBG_EXIT,
"calling backlight_device_unregister()\n"); "calling backlight_device_unregister()\n");
backlight_device_unregister(ibm_backlight_device); backlight_device_unregister(ibm_backlight_device);
ibm_backlight_device = NULL;
} }
} }
...@@ -5764,11 +5782,16 @@ static int __init fan_init(struct ibm_init_struct *iibm) ...@@ -5764,11 +5782,16 @@ static int __init fan_init(struct ibm_init_struct *iibm)
fan_control_access_mode != TPACPI_FAN_WR_NONE) { fan_control_access_mode != TPACPI_FAN_WR_NONE) {
rc = sysfs_create_group(&tpacpi_sensors_pdev->dev.kobj, rc = sysfs_create_group(&tpacpi_sensors_pdev->dev.kobj,
&fan_attr_group); &fan_attr_group);
if (!(rc < 0))
rc = driver_create_file(&tpacpi_hwmon_pdriver.driver,
&driver_attr_fan_watchdog);
if (rc < 0) if (rc < 0)
return rc; return rc;
rc = driver_create_file(&tpacpi_hwmon_pdriver.driver,
&driver_attr_fan_watchdog);
if (rc < 0) {
sysfs_remove_group(&tpacpi_sensors_pdev->dev.kobj,
&fan_attr_group);
return rc;
}
return 0; return 0;
} else } else
return 1; return 1;
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册