X-Git-Url: http://pilppa.org/gitweb/gitweb.cgi?a=blobdiff_plain;f=drivers%2Fmisc%2Fthinkpad_acpi.c;h=9a611402ee9dafa954774c5e8a62f3e175fc16e4;hb=88282ad97860951c39585a1e3dd4cf36d898c4f8;hp=bb8956d0c1047fce4eb35f2323072b8438e59e40;hpb=644b55ce889edd37d6406df26e2d96d7a7390749;p=linux-2.6-omap-h63xx.git diff --git a/drivers/misc/thinkpad_acpi.c b/drivers/misc/thinkpad_acpi.c index bb8956d0c10..ab23a322158 100644 --- a/drivers/misc/thinkpad_acpi.c +++ b/drivers/misc/thinkpad_acpi.c @@ -21,8 +21,8 @@ * 02110-1301, USA. */ -#define IBM_VERSION "0.15" -#define TPACPI_SYSFS_VERSION 0x010000 +#define IBM_VERSION "0.17" +#define TPACPI_SYSFS_VERSION 0x020000 /* * Changelog: @@ -117,6 +117,12 @@ IBM_BIOS_MODULE_ALIAS("K[U,X-Z]"); #define __unused __attribute__ ((unused)) +static enum { + TPACPI_LIFE_INIT = 0, + TPACPI_LIFE_RUNNING, + TPACPI_LIFE_EXITING, +} tpacpi_lifecycle; + /**************************************************************************** **************************************************************************** * @@ -342,6 +348,9 @@ static void dispatch_acpi_notify(acpi_handle handle, u32 event, void *data) { struct ibm_struct *ibm = data; + if (tpacpi_lifecycle != TPACPI_LIFE_RUNNING) + return; + if (!ibm || !ibm->acpi || !ibm->acpi->notify) return; @@ -517,8 +526,10 @@ static char *next_cmd(char **cmds) ****************************************************************************/ static struct platform_device *tpacpi_pdev; -static struct class_device *tpacpi_hwmon; +static struct platform_device *tpacpi_sensors_pdev; +static struct device *tpacpi_hwmon; static struct input_dev *tpacpi_inputdev; +static struct mutex tpacpi_inputdev_send_mutex; static int tpacpi_resume_handler(struct platform_device *pdev) @@ -543,6 +554,12 @@ static struct platform_driver tpacpi_pdriver = { .resume = tpacpi_resume_handler, }; +static struct platform_driver tpacpi_hwmon_pdriver = { + .driver = { + .name = IBM_HWMON_DRVR_NAME, + .owner = THIS_MODULE, + }, +}; /************************************************************************* * thinkpad-acpi driver attributes @@ -692,6 +709,8 @@ static int parse_strtoul(const char *buf, { char *endp; + while (*buf && isspace(*buf)) + buf++; *value = simple_strtoul(buf, &endp, 0); while (*endp && isspace(*endp)) endp++; @@ -906,9 +925,26 @@ static ssize_t hotkey_radio_sw_show(struct device *dev, static struct device_attribute dev_attr_hotkey_radio_sw = __ATTR(hotkey_radio_sw, S_IRUGO, hotkey_radio_sw_show, NULL); +/* sysfs hotkey report_mode -------------------------------------------- */ +static ssize_t hotkey_report_mode_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%d\n", + (hotkey_report_mode != 0) ? hotkey_report_mode : 1); +} + +static struct device_attribute dev_attr_hotkey_report_mode = + __ATTR(hotkey_report_mode, S_IRUGO, hotkey_report_mode_show, NULL); + /* --------------------------------------------------------------------- */ -static struct attribute *hotkey_mask_attributes[] = { +static struct attribute *hotkey_attributes[] __initdata = { + &dev_attr_hotkey_enable.attr, + &dev_attr_hotkey_report_mode.attr, +}; + +static struct attribute *hotkey_mask_attributes[] __initdata = { &dev_attr_hotkey_mask.attr, &dev_attr_hotkey_bios_enabled.attr, &dev_attr_hotkey_bios_mask.attr, @@ -972,6 +1008,7 @@ static int __init hotkey_init(struct ibm_init_struct *iibm) int res, i; int status; + int hkeyv; vdbg_printk(TPACPI_DBG_INIT, "initializing hotkey subdriver\n"); @@ -987,27 +1024,45 @@ static int __init hotkey_init(struct ibm_init_struct *iibm) str_supported(tp_features.hotkey)); if (tp_features.hotkey) { - hotkey_dev_attributes = create_attr_set(7, NULL); + hotkey_dev_attributes = create_attr_set(8, NULL); if (!hotkey_dev_attributes) return -ENOMEM; - res = add_to_attr_set(hotkey_dev_attributes, - &dev_attr_hotkey_enable.attr); + 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, - A30, R30, R31, T20-22, X20-21, X22-24 */ - tp_features.hotkey_mask = - acpi_evalf(hkey_handle, NULL, "DHKN", "qv"); + A30, R30, R31, T20-22, X20-21, X22-24. Detected by checking + for HKEY interface version 0x100 */ + if (acpi_evalf(hkey_handle, &hkeyv, "MHKV", "qd")) { + if ((hkeyv >> 8) != 1) { + printk(IBM_ERR "unknown version of the " + "HKEY interface: 0x%x\n", hkeyv); + printk(IBM_ERR "please report this to %s\n", + IBM_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", str_supported(tp_features.hotkey_mask)); if (tp_features.hotkey_mask) { - /* MHKA available in A31, R40, R40e, T4x, X31, and later */ if (!acpi_evalf(hkey_handle, &hotkey_all_mask, - "MHKA", "qd")) + "MHKA", "qd")) { + printk(IBM_ERR + "missing MHKA handler, " + "please report this to %s\n", + IBM_MAIL); hotkey_all_mask = 0x080cU; /* FN+F12, FN+F4, FN+F3 */ + } } res = hotkey_get(&hotkey_orig_status, &hotkey_orig_mask); @@ -1055,11 +1110,6 @@ static int __init hotkey_init(struct ibm_init_struct *iibm) TPACPI_HOTKEY_MAP_SIZE); } -#ifndef CONFIG_THINKPAD_ACPI_INPUT_ENABLED - for (i = 0; i < 12; i++) - hotkey_keycode_map[i] = KEY_UNKNOWN; -#endif /* ! CONFIG_THINKPAD_ACPI_INPUT_ENABLED */ - set_bit(EV_KEY, tpacpi_inputdev->evbit); set_bit(EV_MSC, tpacpi_inputdev->evbit); set_bit(MSC_SCAN, tpacpi_inputdev->mscbit); @@ -1081,14 +1131,17 @@ static int __init hotkey_init(struct ibm_init_struct *iibm) set_bit(SW_RADIO, tpacpi_inputdev->swbit); } -#ifdef CONFIG_THINKPAD_ACPI_INPUT_ENABLED dbg_printk(TPACPI_DBG_INIT, "enabling hot key handling\n"); res = hotkey_set(1, (hotkey_all_mask & ~hotkey_reserved_mask) | hotkey_orig_mask); if (res) return res; -#endif /* CONFIG_THINKPAD_ACPI_INPUT_ENABLED */ + + dbg_printk(TPACPI_DBG_INIT, + "legacy hot key reporting over procfs %s\n", + (hotkey_report_mode < 2) ? + "enabled" : "disabled"); } return (tp_features.hotkey)? 0 : 1; @@ -1115,6 +1168,8 @@ static void tpacpi_input_send_key(unsigned int scancode, unsigned int keycode) { if (keycode != KEY_RESERVED) { + mutex_lock(&tpacpi_inputdev_send_mutex); + input_report_key(tpacpi_inputdev, keycode, 1); if (keycode == KEY_UNKNOWN) input_event(tpacpi_inputdev, EV_MSC, MSC_SCAN, @@ -1126,6 +1181,8 @@ static void tpacpi_input_send_key(unsigned int scancode, input_event(tpacpi_inputdev, EV_MSC, MSC_SCAN, scancode); input_sync(tpacpi_inputdev); + + mutex_unlock(&tpacpi_inputdev_send_mutex); } } @@ -1133,67 +1190,102 @@ static void tpacpi_input_send_radiosw(void) { int wlsw; - if (tp_features.hotkey_wlsw && !hotkey_get_wlsw(&wlsw)) + mutex_lock(&tpacpi_inputdev_send_mutex); + + if (tp_features.hotkey_wlsw && !hotkey_get_wlsw(&wlsw)) { input_report_switch(tpacpi_inputdev, SW_RADIO, !!wlsw); + input_sync(tpacpi_inputdev); + } + + mutex_unlock(&tpacpi_inputdev_send_mutex); } static void hotkey_notify(struct ibm_struct *ibm, u32 event) { u32 hkey; unsigned int keycode, scancode; - int sendacpi = 1; - - if (event == 0x80 && acpi_evalf(hkey_handle, &hkey, "MHKP", "d")) { - if (tpacpi_inputdev->users > 0) { - switch (hkey >> 12) { - case 1: - /* 0x1000-0x1FFF: key presses */ - scancode = hkey & 0xfff; - if (scancode > 0 && scancode < 0x21) { - scancode--; - keycode = hotkey_keycode_map[scancode]; - tpacpi_input_send_key(scancode, keycode); - sendacpi = (keycode == KEY_RESERVED - || keycode == KEY_UNKNOWN); - } else { - printk(IBM_ERR - "hotkey 0x%04x out of range for keyboard map\n", - hkey); - } - break; - case 5: - /* 0x5000-0x5FFF: LID */ - /* we don't handle it through this path, just - * eat up known LID events */ - if (hkey != 0x5001 && hkey != 0x5002) { - printk(IBM_ERR - "unknown LID-related hotkey event: 0x%04x\n", - hkey); - } + int send_acpi_ev; + int ignore_acpi_ev; + + if (event != 0x80) { + printk(IBM_ERR "unknown HKEY notification event %d\n", event); + /* forward it to userspace, maybe it knows how to handle it */ + acpi_bus_generate_netlink_event(ibm->acpi->device->pnp.device_class, + ibm->acpi->device->dev.bus_id, + event, 0); + return; + } + + while (1) { + if (!acpi_evalf(hkey_handle, &hkey, "MHKP", "d")) { + printk(IBM_ERR "failed to retrieve HKEY event\n"); + return; + } + + if (hkey == 0) { + /* queue empty */ + return; + } + + send_acpi_ev = 0; + ignore_acpi_ev = 0; + + switch (hkey >> 12) { + case 1: + /* 0x1000-0x1FFF: key presses */ + scancode = hkey & 0xfff; + if (scancode > 0 && scancode < 0x21) { + scancode--; + keycode = hotkey_keycode_map[scancode]; + tpacpi_input_send_key(scancode, keycode); + } else { + printk(IBM_ERR + "hotkey 0x%04x out of range for keyboard map\n", + hkey); + send_acpi_ev = 1; + } + break; + case 5: + /* 0x5000-0x5FFF: LID */ + /* we don't handle it through this path, just + * eat up known LID events */ + if (hkey != 0x5001 && hkey != 0x5002) { + printk(IBM_ERR + "unknown LID-related HKEY event: 0x%04x\n", + hkey); + send_acpi_ev = 1; + } else { + ignore_acpi_ev = 1; + } + break; + case 7: + /* 0x7000-0x7FFF: misc */ + if (tp_features.hotkey_wlsw && hkey == 0x7000) { + tpacpi_input_send_radiosw(); break; - case 7: - /* 0x7000-0x7FFF: misc */ - if (tp_features.hotkey_wlsw && hkey == 0x7000) { - tpacpi_input_send_radiosw(); - sendacpi = 0; - break; - } - /* fallthrough to default */ - default: - /* case 2: dock-related */ - /* 0x2305 - T43 waking up due to bay lever eject while aslept */ - /* case 3: ultra-bay related. maybe bay in dock? */ - /* 0x3003 - T43 after wake up by bay lever eject (0x2305) */ - printk(IBM_NOTICE "unhandled hotkey event 0x%04x\n", hkey); } + /* fallthrough to default */ + default: + /* case 2: dock-related */ + /* 0x2305 - T43 waking up due to bay lever eject while aslept */ + /* case 3: ultra-bay related. maybe bay in dock? */ + /* 0x3003 - T43 after wake up by bay lever eject (0x2305) */ + printk(IBM_NOTICE "unhandled HKEY event 0x%04x\n", hkey); + send_acpi_ev = 1; } - if (sendacpi) + /* Legacy events */ + if (!ignore_acpi_ev && (send_acpi_ev || hotkey_report_mode < 2)) { acpi_bus_generate_proc_event(ibm->acpi->device, event, hkey); - } else { - printk(IBM_ERR "unknown hotkey notification event %d\n", event); - acpi_bus_generate_proc_event(ibm->acpi->device, event, 0); + } + + /* netlink events */ + if (!ignore_acpi_ev && send_acpi_ev) { + acpi_bus_generate_netlink_event(ibm->acpi->device->pnp.device_class, + ibm->acpi->device->dev.bus_id, + event, hkey); + } } } @@ -1250,9 +1342,8 @@ static int hotkey_read(char *p) return len; } - res = mutex_lock_interruptible(&hotkey_mutex); - if (res < 0) - return res; + if (mutex_lock_interruptible(&hotkey_mutex)) + return -ERESTARTSYS; res = hotkey_get(&status, &mask); mutex_unlock(&hotkey_mutex); if (res) @@ -1281,9 +1372,8 @@ static int hotkey_write(char *buf) if (!tp_features.hotkey) return -ENODEV; - res = mutex_lock_interruptible(&hotkey_mutex); - if (res < 0) - return res; + if (mutex_lock_interruptible(&hotkey_mutex)) + return -ERESTARTSYS; res = hotkey_get(&status, &mask); if (res) @@ -2789,7 +2879,7 @@ static int __init thermal_init(struct ibm_init_struct *iibm) switch(thermal_read_mode) { case TPACPI_THERMAL_TPEC_16: - res = sysfs_create_group(&tpacpi_pdev->dev.kobj, + res = sysfs_create_group(&tpacpi_sensors_pdev->dev.kobj, &thermal_temp_input16_group); if (res) return res; @@ -2797,7 +2887,7 @@ static int __init thermal_init(struct ibm_init_struct *iibm) case TPACPI_THERMAL_TPEC_8: case TPACPI_THERMAL_ACPI_TMP07: case TPACPI_THERMAL_ACPI_UPDT: - res = sysfs_create_group(&tpacpi_pdev->dev.kobj, + res = sysfs_create_group(&tpacpi_sensors_pdev->dev.kobj, &thermal_temp_input8_group); if (res) return res; @@ -2814,13 +2904,13 @@ static void thermal_exit(void) { switch(thermal_read_mode) { case TPACPI_THERMAL_TPEC_16: - sysfs_remove_group(&tpacpi_pdev->dev.kobj, + sysfs_remove_group(&tpacpi_sensors_pdev->dev.kobj, &thermal_temp_input16_group); break; case TPACPI_THERMAL_TPEC_8: case TPACPI_THERMAL_ACPI_TMP07: case TPACPI_THERMAL_ACPI_UPDT: - sysfs_remove_group(&tpacpi_pdev->dev.kobj, + sysfs_remove_group(&tpacpi_sensors_pdev->dev.kobj, &thermal_temp_input16_group); break; case TPACPI_THERMAL_NONE: @@ -3022,6 +3112,99 @@ static struct backlight_ops ibm_backlight_data = { static struct mutex brightness_mutex; +static int __init tpacpi_query_bcll_levels(acpi_handle handle) +{ + struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; + union acpi_object *obj; + int rc; + + if (ACPI_SUCCESS(acpi_evaluate_object(handle, NULL, NULL, &buffer))) { + obj = (union acpi_object *)buffer.pointer; + if (!obj || (obj->type != ACPI_TYPE_PACKAGE)) { + printk(IBM_ERR "Unknown BCLL data, " + "please report this to %s\n", IBM_MAIL); + rc = 0; + } else { + rc = obj->package.count; + } + } else { + return 0; + } + + kfree(buffer.pointer); + return rc; +} + +static acpi_status __init brightness_find_bcll(acpi_handle handle, u32 lvl, + void *context, void **rv) +{ + char name[ACPI_PATH_SEGMENT_LENGTH]; + struct acpi_buffer buffer = { sizeof(name), &name }; + + if (ACPI_SUCCESS(acpi_get_name(handle, ACPI_SINGLE_NAME, &buffer)) && + !strncmp("BCLL", name, sizeof(name) - 1)) { + if (tpacpi_query_bcll_levels(handle) == 16) { + *rv = handle; + return AE_CTRL_TERMINATE; + } else { + return AE_OK; + } + } else { + return AE_OK; + } +} + +static int __init brightness_check_levels(void) +{ + int status; + void *found_node = NULL; + + if (!vid_handle) { + IBM_ACPIHANDLE_INIT(vid); + } + if (!vid_handle) + return 0; + + /* Search for a BCLL package with 16 levels */ + status = acpi_walk_namespace(ACPI_TYPE_PACKAGE, vid_handle, 3, + brightness_find_bcll, NULL, &found_node); + + return (ACPI_SUCCESS(status) && found_node != NULL); +} + +static acpi_status __init brightness_find_bcl(acpi_handle handle, u32 lvl, + void *context, void **rv) +{ + char name[ACPI_PATH_SEGMENT_LENGTH]; + struct acpi_buffer buffer = { sizeof(name), &name }; + + if (ACPI_SUCCESS(acpi_get_name(handle, ACPI_SINGLE_NAME, &buffer)) && + !strncmp("_BCL", name, sizeof(name) - 1)) { + *rv = handle; + return AE_CTRL_TERMINATE; + } else { + return AE_OK; + } +} + +static int __init brightness_check_std_acpi_support(void) +{ + int status; + void *found_node = NULL; + + if (!vid_handle) { + IBM_ACPIHANDLE_INIT(vid); + } + if (!vid_handle) + return 0; + + /* Search for a _BCL method, but don't execute it */ + status = acpi_walk_namespace(ACPI_TYPE_METHOD, vid_handle, 3, + brightness_find_bcl, NULL, &found_node); + + return (ACPI_SUCCESS(status) && found_node != NULL); +} + static int __init brightness_init(struct ibm_init_struct *iibm) { int b; @@ -3030,6 +3213,18 @@ static int __init brightness_init(struct ibm_init_struct *iibm) mutex_init(&brightness_mutex); + if (!brightness_enable) { + dbg_printk(TPACPI_DBG_INIT, + "brightness support disabled by module parameter\n"); + return 1; + } else if (brightness_enable > 1) { + if (brightness_check_std_acpi_support()) { + printk(IBM_NOTICE + "standard ACPI backlight interface available, not loading native one...\n"); + return 1; + } + } + if (!brightness_mode) { if (thinkpad_id.vendor == PCI_VENDOR_ID_LENOVO) brightness_mode = 2; @@ -3043,10 +3238,17 @@ static int __init brightness_init(struct ibm_init_struct *iibm) if (brightness_mode > 3) return -EINVAL; + tp_features.bright_16levels = + thinkpad_id.vendor == PCI_VENDOR_ID_LENOVO && + brightness_check_levels(); + b = brightness_get(NULL); if (b < 0) return 1; + if (tp_features.bright_16levels) + printk(IBM_INFO "detected a 16-level brightness capable ThinkPad\n"); + ibm_backlight_device = backlight_device_register( TPACPI_BACKLIGHT_DEV_NAME, NULL, NULL, &ibm_backlight_data); @@ -3056,7 +3258,8 @@ static int __init brightness_init(struct ibm_init_struct *iibm) } vdbg_printk(TPACPI_DBG_INIT, "brightness is supported\n"); - ibm_backlight_device->props.max_brightness = 7; + ibm_backlight_device->props.max_brightness = + (tp_features.bright_16levels)? 15 : 7; ibm_backlight_device->props.brightness = b; backlight_update_status(ibm_backlight_device); @@ -3075,6 +3278,8 @@ static void brightness_exit(void) static int brightness_update_status(struct backlight_device *bd) { + /* it is the backlight class's job (caller) to handle + * EINTR and other errors properly */ return brightness_set( (bd->props.fb_blank == FB_BLANK_UNBLANK && bd->props.power == FB_BLANK_UNBLANK) ? @@ -3092,13 +3297,14 @@ static int brightness_get(struct backlight_device *bd) if (brightness_mode & 1) { if (!acpi_ec_read(brightness_offset, &lec)) return -EIO; - lec &= 7; + lec &= (tp_features.bright_16levels)? 0x0f : 0x07; level = lec; }; if (brightness_mode & 2) { lcmos = (nvram_read_byte(TP_NVRAM_ADDR_BRIGHTNESS) & TP_NVRAM_MASK_LEVEL_BRIGHTNESS) >> TP_NVRAM_POS_LEVEL_BRIGHTNESS; + lcmos &= (tp_features.bright_16levels)? 0x0f : 0x07; level = lcmos; } @@ -3114,12 +3320,13 @@ static int brightness_get(struct backlight_device *bd) return level; } +/* May return EINTR which can always be mapped to ERESTARTSYS */ static int brightness_set(int value) { int cmos_cmd, inc, i, res; int current_value; - if (value > 7) + if (value > ((tp_features.bright_16levels)? 15 : 7)) return -EINVAL; res = mutex_lock_interruptible(&brightness_mutex); @@ -3135,7 +3342,7 @@ static int brightness_set(int value) cmos_cmd = value > current_value ? TP_CMOS_BRIGHTNESS_UP : TP_CMOS_BRIGHTNESS_DOWN; - inc = value > current_value ? 1 : -1; + inc = (value > current_value)? 1 : -1; res = 0; for (i = current_value; i != value; i += inc) { @@ -3164,10 +3371,11 @@ static int brightness_read(char *p) if ((level = brightness_get(NULL)) < 0) { len += sprintf(p + len, "level:\t\tunreadable\n"); } else { - len += sprintf(p + len, "level:\t\t%d\n", level & 0x7); + len += sprintf(p + len, "level:\t\t%d\n", level); len += sprintf(p + len, "commands:\tup, down\n"); len += sprintf(p + len, "commands:\tlevel " - " ( is 0-7)\n"); + " ( is 0-%d)\n", + (tp_features.bright_16levels) ? 15 : 7); } return len; @@ -3176,28 +3384,34 @@ static int brightness_read(char *p) static int brightness_write(char *buf) { int level; - int new_level; + int rc; char *cmd; + int max_level = (tp_features.bright_16levels) ? 15 : 7; - while ((cmd = next_cmd(&buf))) { - if ((level = brightness_get(NULL)) < 0) - return level; - level &= 7; + level = brightness_get(NULL); + if (level < 0) + return level; + while ((cmd = next_cmd(&buf))) { if (strlencmp(cmd, "up") == 0) { - new_level = level == 7 ? 7 : level + 1; + if (level < max_level) + level++; } else if (strlencmp(cmd, "down") == 0) { - new_level = level == 0 ? 0 : level - 1; - } else if (sscanf(cmd, "level %d", &new_level) == 1 && - new_level >= 0 && new_level <= 7) { - /* new_level set */ + if (level > 0) + level--; + } else if (sscanf(cmd, "level %d", &level) == 1 && + level >= 0 && level <= max_level) { + /* new level set */ } else return -EINVAL; - - brightness_set(new_level); } - return 0; + /* + * Now we know what the final level should be, so we try to set it. + * Doing it this way makes the syscall restartable in case of EINTR + */ + rc = brightness_set(level); + return (rc == -EINTR)? ERESTARTSYS : rc; } static struct ibm_struct brightness_driver_data = { @@ -3560,9 +3774,8 @@ static ssize_t fan_pwm1_store(struct device *dev, /* scale down from 0-255 to 0-7 */ newlevel = (s >> 5) & 0x07; - rc = mutex_lock_interruptible(&fan_mutex); - if (rc < 0) - return rc; + if (mutex_lock_interruptible(&fan_mutex)) + return -ERESTARTSYS; rc = fan_get_status(&status); if (!rc && (status & @@ -3603,7 +3816,7 @@ static struct device_attribute dev_attr_fan_fan1_input = __ATTR(fan1_input, S_IRUGO, fan_fan1_input_show, NULL); -/* sysfs fan fan_watchdog (driver) ------------------------------------- */ +/* sysfs fan fan_watchdog (hwmon driver) ------------------------------- */ static ssize_t fan_fan_watchdog_show(struct device_driver *drv, char *buf) { @@ -3745,10 +3958,10 @@ static int __init fan_init(struct ibm_init_struct *iibm) if (fan_status_access_mode != TPACPI_FAN_NONE || fan_control_access_mode != TPACPI_FAN_WR_NONE) { - rc = sysfs_create_group(&tpacpi_pdev->dev.kobj, + rc = sysfs_create_group(&tpacpi_sensors_pdev->dev.kobj, &fan_attr_group); if (!(rc < 0)) - rc = driver_create_file(&tpacpi_pdriver.driver, + rc = driver_create_file(&tpacpi_hwmon_pdriver.driver, &driver_attr_fan_watchdog); if (rc < 0) return rc; @@ -3812,9 +4025,8 @@ static int fan_get_status_safe(u8 *status) int rc; u8 s; - rc = mutex_lock_interruptible(&fan_mutex); - if (rc < 0) - return rc; + if (mutex_lock_interruptible(&fan_mutex)) + return -ERESTARTSYS; rc = fan_get_status(&s); if (!rc) fan_update_desired_level(s); @@ -3831,8 +4043,8 @@ static void fan_exit(void) vdbg_printk(TPACPI_DBG_EXIT, "cancelling any pending fan watchdog tasks\n"); /* FIXME: can we really do this unconditionally? */ - sysfs_remove_group(&tpacpi_pdev->dev.kobj, &fan_attr_group); - driver_remove_file(&tpacpi_pdriver.driver, &driver_attr_fan_watchdog); + sysfs_remove_group(&tpacpi_sensors_pdev->dev.kobj, &fan_attr_group); + driver_remove_file(&tpacpi_hwmon_pdriver.driver, &driver_attr_fan_watchdog); cancel_delayed_work(&fan_watchdog_task); flush_scheduled_work(); @@ -3865,6 +4077,9 @@ static void fan_watchdog_fire(struct work_struct *ignored) { int rc; + if (tpacpi_lifecycle != TPACPI_LIFE_RUNNING) + return; + printk(IBM_NOTICE "fan watchdog: enabling fan\n"); rc = fan_set_enable(); if (rc < 0) { @@ -3885,7 +4100,8 @@ static void fan_watchdog_reset(void) if (fan_watchdog_active) cancel_delayed_work(&fan_watchdog_task); - if (fan_watchdog_maxinterval > 0) { + if (fan_watchdog_maxinterval > 0 && + tpacpi_lifecycle != TPACPI_LIFE_EXITING) { fan_watchdog_active = 1; if (!schedule_delayed_work(&fan_watchdog_task, msecs_to_jiffies(fan_watchdog_maxinterval @@ -3944,9 +4160,8 @@ static int fan_set_level_safe(int level) if (!fan_control_allowed) return -EPERM; - rc = mutex_lock_interruptible(&fan_mutex); - if (rc < 0) - return rc; + if (mutex_lock_interruptible(&fan_mutex)) + return -ERESTARTSYS; if (level == TPACPI_FAN_LAST_LEVEL) level = fan_control_desired_level; @@ -3967,9 +4182,8 @@ static int fan_set_enable(void) if (!fan_control_allowed) return -EPERM; - rc = mutex_lock_interruptible(&fan_mutex); - if (rc < 0) - return rc; + if (mutex_lock_interruptible(&fan_mutex)) + return -ERESTARTSYS; switch (fan_control_access_mode) { case TPACPI_FAN_WR_ACPI_FANS: @@ -4023,9 +4237,8 @@ static int fan_set_disable(void) if (!fan_control_allowed) return -EPERM; - rc = mutex_lock_interruptible(&fan_mutex); - if (rc < 0) - return rc; + if (mutex_lock_interruptible(&fan_mutex)) + return -ERESTARTSYS; rc = 0; switch (fan_control_access_mode) { @@ -4062,9 +4275,8 @@ static int fan_set_speed(int speed) if (!fan_control_allowed) return -EPERM; - rc = mutex_lock_interruptible(&fan_mutex); - if (rc < 0) - return rc; + if (mutex_lock_interruptible(&fan_mutex)) + return -ERESTARTSYS; rc = 0; switch (fan_control_access_mode) { @@ -4279,6 +4491,19 @@ static struct ibm_struct fan_driver_data = { **************************************************************************** ****************************************************************************/ +/* sysfs name ---------------------------------------------------------- */ +static ssize_t thinkpad_acpi_pdev_name_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%s\n", IBM_NAME); +} + +static struct device_attribute dev_attr_thinkpad_acpi_pdev_name = + __ATTR(name, S_IRUGO, thinkpad_acpi_pdev_name_show, NULL); + +/* --------------------------------------------------------------------- */ + /* /proc support */ static struct proc_dir_entry *proc_dir; @@ -4425,7 +4650,7 @@ static void ibm_exit(struct ibm_struct *ibm) static void __init get_thinkpad_model_data(struct thinkpad_id_data *tp) { - struct dmi_device *dev = NULL; + const struct dmi_device *dev = NULL; char ec_fw_string[18]; if (!tp) @@ -4592,9 +4817,15 @@ static int __init set_ibm_param(const char *val, struct kernel_param *kp) unsigned int i; struct ibm_struct *ibm; + if (!kp || !kp->name || !val) + return -EINVAL; + for (i = 0; i < ARRAY_SIZE(ibms_init); i++) { ibm = ibms_init[i].data; - BUG_ON(ibm == NULL); + WARN_ON(ibm == NULL); + + if (!ibm || !ibm->name) + continue; if (strcmp(ibm->name, kp->name) == 0 && ibm->write) { if (strlen(val) > sizeof(ibms_init[i].param) - 2) @@ -4623,6 +4854,12 @@ module_param_named(fan_control, fan_control_allowed, bool, 0); static int brightness_mode; module_param_named(brightness_mode, brightness_mode, int, 0); +static unsigned int brightness_enable = 2; /* 2 = auto, 0 = no, 1 = yes */ +module_param(brightness_enable, uint, 0); + +static unsigned int hotkey_report_mode; +module_param(hotkey_report_mode, uint, 0); + #define IBM_PARAM(feature) \ module_param_call(feature, set_ibm_param, NULL, NULL, 0) @@ -4648,6 +4885,12 @@ static int __init thinkpad_acpi_module_init(void) { int ret, i; + tpacpi_lifecycle = TPACPI_LIFE_INIT; + + /* Parameter checking */ + if (hotkey_report_mode > 2) + return -EINVAL; + /* Driver-level probe */ get_thinkpad_model_data(&thinkpad_id); @@ -4672,19 +4915,31 @@ static int __init thinkpad_acpi_module_init(void) ret = platform_driver_register(&tpacpi_pdriver); if (ret) { - printk(IBM_ERR "unable to register platform driver\n"); + printk(IBM_ERR "unable to register main platform driver\n"); thinkpad_acpi_module_exit(); return ret; } tp_features.platform_drv_registered = 1; + ret = platform_driver_register(&tpacpi_hwmon_pdriver); + if (ret) { + printk(IBM_ERR "unable to register hwmon platform driver\n"); + thinkpad_acpi_module_exit(); + return ret; + } + tp_features.sensors_pdrv_registered = 1; + ret = tpacpi_create_driver_attributes(&tpacpi_pdriver.driver); + if (!ret) { + tp_features.platform_drv_attrs_registered = 1; + ret = tpacpi_create_driver_attributes(&tpacpi_hwmon_pdriver.driver); + } if (ret) { printk(IBM_ERR "unable to create sysfs driver attributes\n"); thinkpad_acpi_module_exit(); return ret; } - tp_features.platform_drv_attrs_registered = 1; + tp_features.sensors_pdrv_attrs_registered = 1; /* Device initialization */ @@ -4697,7 +4952,26 @@ static int __init thinkpad_acpi_module_init(void) thinkpad_acpi_module_exit(); return ret; } - tpacpi_hwmon = hwmon_device_register(&tpacpi_pdev->dev); + tpacpi_sensors_pdev = platform_device_register_simple( + IBM_HWMON_DRVR_NAME, + -1, NULL, 0); + if (IS_ERR(tpacpi_sensors_pdev)) { + ret = PTR_ERR(tpacpi_sensors_pdev); + tpacpi_sensors_pdev = NULL; + printk(IBM_ERR "unable to register hwmon platform device\n"); + thinkpad_acpi_module_exit(); + return ret; + } + ret = device_create_file(&tpacpi_sensors_pdev->dev, + &dev_attr_thinkpad_acpi_pdev_name); + if (ret) { + printk(IBM_ERR + "unable to create sysfs hwmon device attributes\n"); + thinkpad_acpi_module_exit(); + return ret; + } + tp_features.sensors_pdev_attrs_registered = 1; + tpacpi_hwmon = hwmon_device_register(&tpacpi_sensors_pdev->dev); if (IS_ERR(tpacpi_hwmon)) { ret = PTR_ERR(tpacpi_hwmon); tpacpi_hwmon = NULL; @@ -4705,6 +4979,7 @@ static int __init thinkpad_acpi_module_init(void) thinkpad_acpi_module_exit(); return ret; } + mutex_init(&tpacpi_inputdev_send_mutex); tpacpi_inputdev = input_allocate_device(); if (!tpacpi_inputdev) { printk(IBM_ERR "unable to allocate input device\n"); @@ -4739,6 +5014,7 @@ static int __init thinkpad_acpi_module_init(void) tp_features.input_device_registered = 1; } + tpacpi_lifecycle = TPACPI_LIFE_RUNNING; return 0; } @@ -4746,6 +5022,8 @@ static void thinkpad_acpi_module_exit(void) { struct ibm_struct *ibm, *itmp; + tpacpi_lifecycle = TPACPI_LIFE_EXITING; + list_for_each_entry_safe_reverse(ibm, itmp, &tpacpi_all_drivers, all_drivers) { @@ -4764,12 +5042,22 @@ static void thinkpad_acpi_module_exit(void) if (tpacpi_hwmon) hwmon_device_unregister(tpacpi_hwmon); + if (tp_features.sensors_pdev_attrs_registered) + device_remove_file(&tpacpi_sensors_pdev->dev, + &dev_attr_thinkpad_acpi_pdev_name); + if (tpacpi_sensors_pdev) + platform_device_unregister(tpacpi_sensors_pdev); if (tpacpi_pdev) platform_device_unregister(tpacpi_pdev); + if (tp_features.sensors_pdrv_attrs_registered) + tpacpi_remove_driver_attributes(&tpacpi_hwmon_pdriver.driver); if (tp_features.platform_drv_attrs_registered) tpacpi_remove_driver_attributes(&tpacpi_pdriver.driver); + if (tp_features.sensors_pdrv_registered) + platform_driver_unregister(&tpacpi_hwmon_pdriver); + if (tp_features.platform_drv_registered) platform_driver_unregister(&tpacpi_pdriver);