diff options
Diffstat (limited to 'drivers/misc/thinkpad_acpi.c')
-rw-r--r-- | drivers/misc/thinkpad_acpi.c | 6949 |
1 files changed, 0 insertions, 6949 deletions
diff --git a/drivers/misc/thinkpad_acpi.c b/drivers/misc/thinkpad_acpi.c deleted file mode 100644 index 899766e16fa..00000000000 --- a/drivers/misc/thinkpad_acpi.c +++ /dev/null @@ -1,6949 +0,0 @@ -/* - * thinkpad_acpi.c - ThinkPad ACPI Extras - * - * - * Copyright (C) 2004-2005 Borislav Deianov <borislav@users.sf.net> - * Copyright (C) 2006-2008 Henrique de Moraes Holschuh <hmh@hmh.eng.br> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA - * 02110-1301, USA. - */ - -#define TPACPI_VERSION "0.21" -#define TPACPI_SYSFS_VERSION 0x020200 - -/* - * 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> - */ - -#include <linux/kernel.h> -#include <linux/module.h> -#include <linux/init.h> -#include <linux/types.h> -#include <linux/string.h> -#include <linux/list.h> -#include <linux/mutex.h> -#include <linux/kthread.h> -#include <linux/freezer.h> -#include <linux/delay.h> - -#include <linux/nvram.h> -#include <linux/proc_fs.h> -#include <linux/sysfs.h> -#include <linux/backlight.h> -#include <linux/fb.h> -#include <linux/platform_device.h> -#include <linux/hwmon.h> -#include <linux/hwmon-sysfs.h> -#include <linux/input.h> -#include <linux/leds.h> -#include <linux/rfkill.h> -#include <asm/uaccess.h> - -#include <linux/dmi.h> -#include <linux/jiffies.h> -#include <linux/workqueue.h> - -#include <acpi/acpi_drivers.h> -#include <acpi/acnamesp.h> - -#include <linux/pci_ids.h> - - -/* ThinkPad CMOS commands */ -#define TP_CMOS_VOLUME_DOWN 0 -#define TP_CMOS_VOLUME_UP 1 -#define TP_CMOS_VOLUME_MUTE 2 -#define TP_CMOS_BRIGHTNESS_UP 4 -#define TP_CMOS_BRIGHTNESS_DOWN 5 -#define TP_CMOS_THINKLIGHT_ON 12 -#define TP_CMOS_THINKLIGHT_OFF 13 - -/* NVRAM Addresses */ -enum tp_nvram_addr { - TP_NVRAM_ADDR_HK2 = 0x57, - TP_NVRAM_ADDR_THINKLIGHT = 0x58, - TP_NVRAM_ADDR_VIDEO = 0x59, - TP_NVRAM_ADDR_BRIGHTNESS = 0x5e, - TP_NVRAM_ADDR_MIXER = 0x60, -}; - -/* NVRAM bit masks */ -enum { - TP_NVRAM_MASK_HKT_THINKPAD = 0x08, - TP_NVRAM_MASK_HKT_ZOOM = 0x20, - TP_NVRAM_MASK_HKT_DISPLAY = 0x40, - TP_NVRAM_MASK_HKT_HIBERNATE = 0x80, - TP_NVRAM_MASK_THINKLIGHT = 0x10, - TP_NVRAM_MASK_HKT_DISPEXPND = 0x30, - TP_NVRAM_MASK_HKT_BRIGHTNESS = 0x20, - TP_NVRAM_MASK_LEVEL_BRIGHTNESS = 0x0f, - TP_NVRAM_POS_LEVEL_BRIGHTNESS = 0, - TP_NVRAM_MASK_MUTE = 0x40, - TP_NVRAM_MASK_HKT_VOLUME = 0x80, - TP_NVRAM_MASK_LEVEL_VOLUME = 0x0f, - TP_NVRAM_POS_LEVEL_VOLUME = 0, -}; - -/* ACPI HIDs */ -#define TPACPI_ACPI_HKEY_HID "IBM0068" - -/* Input IDs */ -#define TPACPI_HKEY_INPUT_PRODUCT 0x5054 /* "TP" */ -#define TPACPI_HKEY_INPUT_VERSION 0x4101 - - -/**************************************************************************** - * Main driver - */ - -#define TPACPI_NAME "thinkpad" -#define TPACPI_DESC "ThinkPad ACPI Extras" -#define TPACPI_FILE TPACPI_NAME "_acpi" -#define TPACPI_URL "http://ibm-acpi.sf.net/" -#define TPACPI_MAIL "ibm-acpi-devel@lists.sourceforge.net" - -#define TPACPI_PROC_DIR "ibm" -#define TPACPI_ACPI_EVENT_PREFIX "ibm" -#define TPACPI_DRVR_NAME TPACPI_FILE -#define TPACPI_DRVR_SHORTNAME "tpacpi" -#define TPACPI_HWMON_DRVR_NAME TPACPI_NAME "_hwmon" - -#define TPACPI_NVRAM_KTHREAD_NAME "ktpacpi_nvramd" -#define TPACPI_WORKQUEUE_NAME "ktpacpid" - -#define TPACPI_MAX_ACPI_ARGS 3 - -/* rfkill switches */ -enum { - TPACPI_RFK_BLUETOOTH_SW_ID = 0, - TPACPI_RFK_WWAN_SW_ID, -}; - -/* Debugging */ -#define TPACPI_LOG TPACPI_FILE ": " -#define TPACPI_ERR KERN_ERR TPACPI_LOG -#define TPACPI_NOTICE KERN_NOTICE TPACPI_LOG -#define TPACPI_INFO KERN_INFO TPACPI_LOG -#define TPACPI_DEBUG KERN_DEBUG TPACPI_LOG - -#define TPACPI_DBG_ALL 0xffff -#define TPACPI_DBG_INIT 0x0001 -#define TPACPI_DBG_EXIT 0x0002 -#define dbg_printk(a_dbg_level, format, arg...) \ - do { if (dbg_level & a_dbg_level) \ - printk(TPACPI_DEBUG "%s: " format, __func__ , ## arg); \ - } while (0) -#ifdef CONFIG_THINKPAD_ACPI_DEBUG -#define vdbg_printk(a_dbg_level, format, arg...) \ - dbg_printk(a_dbg_level, format, ## arg) -static const char *str_supported(int is_supported); -#else -#define vdbg_printk(a_dbg_level, format, arg...) -#endif - -#define onoff(status, bit) ((status) & (1 << (bit)) ? "on" : "off") -#define enabled(status, bit) ((status) & (1 << (bit)) ? "enabled" : "disabled") -#define strlencmp(a, b) (strncmp((a), (b), strlen(b))) - - -/**************************************************************************** - * Driver-wide structs and misc. variables - */ - -struct ibm_struct; - -struct tp_acpi_drv_struct { - const struct acpi_device_id *hid; - struct acpi_driver *driver; - - void (*notify) (struct ibm_struct *, u32); - acpi_handle *handle; - u32 type; - struct acpi_device *device; -}; - -struct ibm_struct { - char *name; - - int (*read) (char *); - int (*write) (char *); - void (*exit) (void); - void (*resume) (void); - void (*suspend) (pm_message_t state); - - struct list_head all_drivers; - - struct tp_acpi_drv_struct *acpi; - - struct { - u8 acpi_driver_registered:1; - u8 acpi_notify_installed:1; - u8 proc_created:1; - u8 init_called:1; - u8 experimental:1; - } flags; -}; - -struct ibm_init_struct { - char param[32]; - - int (*init) (struct ibm_init_struct *); - struct ibm_struct *data; -}; - -static struct { -#ifdef CONFIG_THINKPAD_ACPI_BAY - u32 bay_status:1; - u32 bay_eject:1; - u32 bay_status2:1; - u32 bay_eject2:1; -#endif - u32 bluetooth:1; - u32 hotkey:1; - u32 hotkey_mask:1; - u32 hotkey_wlsw:1; - u32 hotkey_tablet:1; - u32 light:1; - u32 light_status:1; - u32 bright_16levels:1; - u32 bright_acpimode:1; - u32 wan:1; - u32 fan_ctrl_status_undef:1; - u32 input_device_registered:1; - u32 platform_drv_registered:1; - u32 platform_drv_attrs_registered:1; - u32 sensors_pdrv_registered:1; - u32 sensors_pdrv_attrs_registered:1; - u32 sensors_pdev_attrs_registered:1; - u32 hotkey_poll_active:1; -} tp_features; - -static struct { - u16 hotkey_mask_ff:1; - u16 bright_cmos_ec_unsync:1; -} tp_warned; - -struct thinkpad_id_data { - unsigned int vendor; /* ThinkPad vendor: - * PCI_VENDOR_ID_IBM/PCI_VENDOR_ID_LENOVO */ - - char *bios_version_str; /* Something like 1ZET51WW (1.03z) */ - char *ec_version_str; /* Something like 1ZHT51WW-1.04a */ - - u16 bios_model; /* Big Endian, TP-1Y = 0x5931, 0 = unknown */ - u16 ec_model; - - char *model_str; /* ThinkPad T43 */ - char *nummodel_str; /* 9384A9C for a 9384-A9C model */ -}; -static struct thinkpad_id_data thinkpad_id; - -static enum { - TPACPI_LIFE_INIT = 0, - TPACPI_LIFE_RUNNING, - TPACPI_LIFE_EXITING, -} tpacpi_lifecycle; - -static int experimental; -static u32 dbg_level; - -static struct workqueue_struct *tpacpi_wq; - -/* Special LED class that can defer work */ -struct tpacpi_led_classdev { - struct led_classdev led_classdev; - struct work_struct work; - enum led_brightness new_brightness; - unsigned int led; -}; - -/**************************************************************************** - **************************************************************************** - * - * ACPI Helpers and device model - * - **************************************************************************** - ****************************************************************************/ - -/************************************************************************* - * ACPI basic handles - */ - -static acpi_handle root_handle; - -#define TPACPI_HANDLE(object, parent, paths...) \ - static acpi_handle object##_handle; \ - static acpi_handle *object##_parent = &parent##_handle; \ - static char *object##_path; \ - static char *object##_paths[] = { paths } - -TPACPI_HANDLE(ec, root, "\\_SB.PCI0.ISA.EC0", /* 240, 240x */ - "\\_SB.PCI.ISA.EC", /* 570 */ - "\\_SB.PCI0.ISA0.EC0", /* 600e/x, 770e, 770x */ - "\\_SB.PCI0.ISA.EC", /* A21e, A2xm/p, T20-22, X20-21 */ - "\\_SB.PCI0.AD4S.EC0", /* i1400, R30 */ - "\\_SB.PCI0.ICH3.EC0", /* R31 */ - "\\_SB.PCI0.LPC.EC", /* all others */ - ); - -TPACPI_HANDLE(ecrd, ec, "ECRD"); /* 570 */ -TPACPI_HANDLE(ecwr, ec, "ECWR"); /* 570 */ - -TPACPI_HANDLE(cmos, root, "\\UCMS", /* R50, R50e, R50p, R51, */ - /* T4x, X31, X40 */ - "\\CMOS", /* A3x, G4x, R32, T23, T30, X22-24, X30 */ - "\\CMS", /* R40, R40e */ - ); /* all others */ - -TPACPI_HANDLE(hkey, ec, "\\_SB.HKEY", /* 600e/x, 770e, 770x */ - "^HKEY", /* R30, R31 */ - "HKEY", /* all others */ - ); /* 570 */ - -TPACPI_HANDLE(vid, root, "\\_SB.PCI.AGP.VGA", /* 570 */ - "\\_SB.PCI0.AGP0.VID0", /* 600e/x, 770x */ - "\\_SB.PCI0.VID0", /* 770e */ - "\\_SB.PCI0.VID", /* A21e, G4x, R50e, X30, X40 */ - "\\_SB.PCI0.AGP.VID", /* all others */ - ); /* R30, R31 */ - - -/************************************************************************* - * ACPI helpers - */ - -static int acpi_evalf(acpi_handle handle, - void *res, char *method, char *fmt, ...) -{ - char *fmt0 = fmt; - struct acpi_object_list params; - union acpi_object in_objs[TPACPI_MAX_ACPI_ARGS]; - struct acpi_buffer result, *resultp; - union acpi_object out_obj; - acpi_status status; - va_list ap; - char res_type; - int success; - int quiet; - - if (!*fmt) { - printk(TPACPI_ERR "acpi_evalf() called with empty format\n"); - return 0; - } - - if (*fmt == 'q') { - quiet = 1; - fmt++; - } else - quiet = 0; - - res_type = *(fmt++); - - params.count = 0; - params.pointer = &in_objs[0]; - - va_start(ap, fmt); - while (*fmt) { - char c = *(fmt++); - switch (c) { - case 'd': /* int */ - in_objs[params.count].integer.value = va_arg(ap, int); - in_objs[params.count++].type = ACPI_TYPE_INTEGER; - break; - /* add more types as needed */ - default: - printk(TPACPI_ERR "acpi_evalf() called " - "with invalid format character '%c'\n", c); - return 0; - } - } - va_end(ap); - - if (res_type != 'v') { - result.length = sizeof(out_obj); - result.pointer = &out_obj; - resultp = &result; - } else - resultp = NULL; - - status = acpi_evaluate_object(handle, method, ¶ms, resultp); - - switch (res_type) { - case 'd': /* int */ - if (res) - *(int *)res = out_obj.integer.value; - success = status == AE_OK && out_obj.type == ACPI_TYPE_INTEGER; - break; - case 'v': /* void */ - success = status == AE_OK; - break; - /* add more types as needed */ - default: - printk(TPACPI_ERR "acpi_evalf() called " - "with invalid format character '%c'\n", res_type); - return 0; - } - - if (!success && !quiet) - printk(TPACPI_ERR "acpi_evalf(%s, %s, ...) failed: %d\n", - method, fmt0, status); - - return success; -} - -static int acpi_ec_read(int i, u8 *p) -{ - int v; - - if (ecrd_handle) { - if (!acpi_evalf(ecrd_handle, &v, NULL, "dd", i)) - return 0; - *p = v; - } else { - if (ec_read(i, p) < 0) - return 0; - } - - return 1; -} - -static int acpi_ec_write(int i, u8 v) -{ - if (ecwr_handle) { - if (!acpi_evalf(ecwr_handle, NULL, NULL, "vdd", i, v)) - return 0; - } else { - if (ec_write(i, v) < 0) - return 0; - } - - return 1; -} - -#if defined(CONFIG_THINKPAD_ACPI_DOCK) || defined(CONFIG_THINKPAD_ACPI_BAY) -static int _sta(acpi_handle handle) -{ - int status; - - if (!handle || !acpi_evalf(handle, &status, "_STA", "d")) - status = 0; - - return status; -} -#endif - -static int issue_thinkpad_cmos_command(int cmos_cmd) -{ - if (!cmos_handle) - return -ENXIO; - - if (!acpi_evalf(cmos_handle, NULL, NULL, "vd", cmos_cmd)) - return -EIO; - - return 0; -} - -/************************************************************************* - * ACPI device model - */ - -#define TPACPI_ACPIHANDLE_INIT(object) \ - drv_acpi_handle_init(#object, &object##_handle, *object##_parent, \ - object##_paths, ARRAY_SIZE(object##_paths), &object##_path) - -static void drv_acpi_handle_init(char *name, - acpi_handle *handle, acpi_handle parent, - char **paths, int num_paths, char **path) -{ - int i; - acpi_status status; - - 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)) { - *path = paths[i]; - dbg_printk(TPACPI_DBG_INIT, - "Found ACPI handle %s for %s\n", - *path, name); - return; - } - } - - vdbg_printk(TPACPI_DBG_INIT, "ACPI handle for %s not found\n", - name); - *handle = NULL; -} - -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; - - ibm->acpi->notify(ibm, event); -} - -static int __init setup_acpi_notify(struct ibm_struct *ibm) -{ - acpi_status status; - int rc; - - BUG_ON(!ibm->acpi); - - if (!*ibm->acpi->handle) - return 0; - - vdbg_printk(TPACPI_DBG_INIT, - "setting up ACPI notify for %s\n", ibm->name); - - rc = acpi_bus_get_device(*ibm->acpi->handle, &ibm->acpi->device); - if (rc < 0) { - printk(TPACPI_ERR "acpi_bus_get_device(%s) failed: %d\n", - ibm->name, rc); - return -ENODEV; - } - - ibm->acpi->device->driver_data = ibm; - sprintf(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); - if (ACPI_FAILURE(status)) { - if (status == AE_ALREADY_EXISTS) { - printk(TPACPI_NOTICE - "another device driver is already " - "handling %s events\n", ibm->name); - } else { - printk(TPACPI_ERR - "acpi_install_notify_handler(%s) failed: %d\n", - ibm->name, status); - } - return -ENODEV; - } - ibm->flags.acpi_notify_installed = 1; - return 0; -} - -static int __init tpacpi_device_add(struct acpi_device *device) -{ - return 0; -} - -static int __init register_tpacpi_subdriver(struct ibm_struct *ibm) -{ - int rc; - - dbg_printk(TPACPI_DBG_INIT, - "registering %s as an ACPI driver\n", ibm->name); - - BUG_ON(!ibm->acpi); - - ibm->acpi->driver = kzalloc(sizeof(struct acpi_driver), GFP_KERNEL); - if (!ibm->acpi->driver) { - printk(TPACPI_ERR - "failed to allocate memory for ibm->acpi->driver\n"); - return -ENOMEM; - } - - sprintf(ibm->acpi->driver->name, "%s_%s", TPACPI_NAME, ibm->name); - ibm->acpi->driver->ids = ibm->acpi->hid; - - ibm->acpi->driver->ops.add = &tpacpi_device_add; - - rc = acpi_bus_register_driver(ibm->acpi->driver); - if (rc < 0) { - printk(TPACPI_ERR "acpi_bus_register_driver(%s) failed: %d\n", - ibm->name, rc); - kfree(ibm->acpi->driver); - ibm->acpi->driver = NULL; - } else if (!rc) - ibm->flags.acpi_driver_registered = 1; - - return rc; -} - - -/**************************************************************************** - **************************************************************************** - * - * Procfs Helpers - * - **************************************************************************** - ****************************************************************************/ - -static int dispatch_procfs_read(char *page, char **start, off_t off, - int count, int *eof, void *data) -{ - struct ibm_struct *ibm = data; - int len; - - if (!ibm || !ibm->read) - return -EINVAL; - - len = ibm->read(page); - if (len < 0) - return len; - - if (len <= off + count) - *eof = 1; - *start = page + off; - len -= off; - if (len > count) - len = count; - if (len < 0) - len = 0; - - return len; -} - -static int dispatch_procfs_write(struct file *file, - const char __user *userbuf, - unsigned long count, void *data) -{ - struct ibm_struct *ibm = data; - char *kernbuf; - int ret; - - if (!ibm || !ibm->write) - return -EINVAL; - - kernbuf = kmalloc(count + 2, GFP_KERNEL); - if (!kernbuf) - return -ENOMEM; - - if (copy_from_user(kernbuf, userbuf, count)) { - kfree(kernbuf); - return -EFAULT; - } - - kernbuf[count] = 0; - strcat(kernbuf, ","); - ret = ibm->write(kernbuf); - if (ret == 0) - ret = count; - - kfree(kernbuf); - - return ret; -} - -static char *next_cmd(char **cmds) -{ - char *start = *cmds; - char *end; - - while ((end = strchr(start, ',')) && end == start) - start = end + 1; - - if (!end) - return NULL; - - *end = 0; - *cmds = end + 1; - return start; -} - - -/**************************************************************************** - **************************************************************************** - * - * Device model: input, hwmon and platform - * - **************************************************************************** - ****************************************************************************/ - -static struct platform_device *tpacpi_pdev; -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 LIST_HEAD(tpacpi_all_drivers); - -static int tpacpi_suspend_handler(struct platform_device *pdev, - pm_message_t state) -{ - struct ibm_struct *ibm, *itmp; - - list_for_each_entry_safe(ibm, itmp, - &tpacpi_all_drivers, - all_drivers) { - if (ibm->suspend) - (ibm->suspend)(state); - } - - return 0; -} - -static int tpacpi_resume_handler(struct platform_device *pdev) -{ - struct ibm_struct *ibm, *itmp; - - list_for_each_entry_safe(ibm, itmp, - &tpacpi_all_drivers, - all_drivers) { - if (ibm->resume) - (ibm->resume)(); - } - - return 0; -} - -static struct platform_driver tpacpi_pdriver = { - .driver = { - .name = TPACPI_DRVR_NAME, - .owner = THIS_MODULE, - }, - .suspend = tpacpi_suspend_handler, - .resume = tpacpi_resume_handler, -}; - -static struct platform_driver tpacpi_hwmon_pdriver = { - .driver = { - .name = TPACPI_HWMON_DRVR_NAME, - .owner = THIS_MODULE, - }, -}; - -/************************************************************************* - * sysfs support helpers - */ - -struct attribute_set { - unsigned int members, max_members; - struct attribute_group group; -}; - -struct attribute_set_obj { - struct attribute_set s; - struct attribute *a; -} __attribute__((packed)); - -static struct attribute_set *create_attr_set(unsigned int max_members, - const char *name) -{ - struct attribute_set_obj *sobj; - - if (max_members == 0) - return NULL; - - /* Allocates space for implicit NULL at the end too */ - sobj = kzalloc(sizeof(struct attribute_set_obj) + - max_members * sizeof(struct attribute *), - GFP_KERNEL); - if (!sobj) - return NULL; - sobj->s.max_members = max_members; - sobj->s.group.attrs = &sobj->a; - sobj->s.group.name = name; - - return &sobj->s; -} - -#define destroy_attr_set(_set) \ - kfree(_set); - -/* not multi-threaded safe, use it in a single thread per set */ -static int add_to_attr_set(struct attribute_set *s, struct attribute *attr) -{ - if (!s || !attr) - return -EINVAL; - - if (s->members >= s->max_members) - return -ENOMEM; - - s->group.attrs[s->members] = attr; - s->members++; - - return 0; -} - -static int add_many_to_attr_set(struct attribute_set *s, - struct attribute **attr, - unsigned int count) -{ - int i, res; - - for (i = 0; i < count; i++) { - res = add_to_attr_set(s, attr[i]); - if (res) - return res; - } - - return 0; -} - -static void delete_attr_set(struct attribute_set *s, struct kobject *kobj) -{ - sysfs_remove_group(kobj, &s->group); - destroy_attr_set(s); -} - -#define register_attr_set_with_sysfs(_attr_set, _kobj) \ - sysfs_create_group(_kobj, &_attr_set->group) - -static int parse_strtoul(const char *buf, - unsigned long max, unsigned long *value) -{ - char *endp; - - while (*buf && isspace(*buf)) - buf++; - *value = simple_strtoul(buf, &endp, 0); - while (*endp && isspace(*endp)) - endp++; - if (*endp || *value > max) - return -EINVAL; - - return 0; -} - -static void tpacpi_disable_brightness_delay(void) -{ - if (acpi_evalf(hkey_handle, NULL, "PWMS", "qvd", 0)) - printk(TPACPI_NOTICE - "ACPI backlight control delay disabled\n"); -} - -static int __init tpacpi_query_bcl_levels(acpi_handle handle) -{ - struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; - union acpi_object *obj; - int rc; - - if (ACPI_SUCCESS(acpi_evaluate_object(handle, NULL, NULL, &buffer))) { - obj = (union acpi_object *)buffer.pointer; - if (!obj || (obj->type != ACPI_TYPE_PACKAGE)) { - printk(TPACPI_ERR "Unknown _BCL data, " - "please report this to %s\n", TPACPI_MAIL); - rc = 0; - } else { - rc = obj->package.count; - } - } else { - return 0; - } - - kfree(buffer.pointer); - return rc; -} - -static acpi_status __init tpacpi_acpi_walk_find_bcl(acpi_handle handle, - u32 lvl, void *context, void **rv) -{ - char name[ACPI_PATH_SEGMENT_LENGTH]; - struct acpi_buffer buffer = { sizeof(name), &name }; - - if (ACPI_SUCCESS(acpi_get_name(handle, ACPI_SINGLE_NAME, &buffer)) && - !strncmp("_BCL", name, sizeof(name) - 1)) { - BUG_ON(!rv || !*rv); - **(int **)rv = tpacpi_query_bcl_levels(handle); - return AE_CTRL_TERMINATE; - } else { - return AE_OK; - } -} - -/* - * Returns 0 (no ACPI _BCL or _BCL invalid), or size of brightness map - */ -static int __init tpacpi_check_std_acpi_brightness_support(void) -{ - int status; - int bcl_levels = 0; - void *bcl_ptr = &bcl_levels; - - if (!vid_handle) { - TPACPI_ACPIHANDLE_INIT(vid); - } - if (!vid_handle) - return 0; - - /* - * Search for a _BCL method, and execute it. This is safe on all - * ThinkPads, and as a side-effect, _BCL will place a Lenovo Vista - * BIOS in ACPI backlight control mode. We do NOT have to care - * about calling the _BCL method in an enabled video device, any - * will do for our purposes. - */ - - status = acpi_walk_namespace(ACPI_TYPE_METHOD, vid_handle, 3, - tpacpi_acpi_walk_find_bcl, NULL, - &bcl_ptr); - - if (ACPI_SUCCESS(status) && bcl_levels > 2) { - tp_features.bright_acpimode = 1; - return (bcl_levels - 2); - } - - return 0; -} - -static int __init tpacpi_new_rfkill(const unsigned int id, - struct rfkill **rfk, - const enum rfkill_type rfktype, - const char *name, - int (*toggle_radio)(void *, enum rfkill_state), - int (*get_state)(void *, enum rfkill_state *)) -{ - int res; - enum rfkill_state initial_state; - - *rfk = rfkill_allocate(&tpacpi_pdev->dev, rfktype); - if (!*rfk) { - printk(TPACPI_ERR - "failed to allocate memory for rfkill class\n"); - return -ENOMEM; - } - - (*rfk)->name = name; - (*rfk)->get_state = get_state; - (*rfk)->toggle_radio = toggle_radio; - - if (!get_state(NULL, &initial_state)) - (*rfk)->state = initial_state; - - res = rfkill_register(*rfk); - if (res < 0) { - printk(TPACPI_ERR - "failed to register %s rfkill switch: %d\n", - name, res); - rfkill_free(*rfk); - *rfk = NULL; - return res; - } - - return 0; -} - -/************************************************************************* - * thinkpad-acpi driver attributes - */ - -/* interface_version --------------------------------------------------- */ -static ssize_t tpacpi_driver_interface_version_show( - struct device_driver *drv, - char *buf) -{ - return snprintf(buf, PAGE_SIZE, "0x%08x\n", TPACPI_SYSFS_VERSION); -} - -static DRIVER_ATTR(interface_version, S_IRUGO, - tpacpi_driver_interface_version_show, NULL); - -/* debug_level --------------------------------------------------------- */ -static ssize_t tpacpi_driver_debug_show(struct device_driver *drv, - char *buf) -{ - return snprintf(buf, PAGE_SIZE, "0x%04x\n", dbg_level); -} - -static ssize_t tpacpi_driver_debug_store(struct device_driver *drv, - const char *buf, size_t count) -{ - unsigned long t; - - if (parse_strtoul(buf, 0xffff, &t)) - return -EINVAL; - - dbg_level = t; - - return count; -} - -static DRIVER_ATTR(debug_level, S_IWUSR | S_IRUGO, - tpacpi_driver_debug_show, tpacpi_driver_debug_store); - -/* version ------------------------------------------------------------- */ -static ssize_t tpacpi_driver_version_show(struct device_driver *drv, - char *buf) -{ - return snprintf(buf, PAGE_SIZE, "%s v%s\n", - TPACPI_DESC, TPACPI_VERSION); -} - -static DRIVER_ATTR(version, S_IRUGO, - tpacpi_driver_version_show, NULL); - -/* --------------------------------------------------------------------- */ - -static struct driver_attribute *tpacpi_driver_attributes[] = { - &driver_attr_debug_level, &driver_attr_version, - &driver_attr_interface_version, -}; - -static int __init tpacpi_create_driver_attributes(struct device_driver *drv) -{ - int i, res; - - i = 0; - res = 0; - while (!res && i < ARRAY_SIZE(tpacpi_driver_attributes)) { - res = driver_create_file(drv, tpacpi_driver_attributes[i]); - i++; - } - - return res; -} - -static void tpacpi_remove_driver_attributes(struct device_driver *drv) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(tpacpi_driver_attributes); i++) - driver_remove_file(drv, tpacpi_driver_attributes[i]); -} - -/**************************************************************************** - **************************************************************************** - * - * Subdrivers - * - **************************************************************************** - ****************************************************************************/ - -/************************************************************************* - * thinkpad-acpi init subdriver - */ - -static int __init thinkpad_acpi_driver_init(struct ibm_init_struct *iibm) -{ - printk(TPACPI_INFO "%s v%s\n", TPACPI_DESC, TPACPI_VERSION); - printk(TPACPI_INFO "%s\n", TPACPI_URL); - - printk(TPACPI_INFO "ThinkPad BIOS %s, EC %s\n", - (thinkpad_id.bios_version_str) ? - thinkpad_id.bios_version_str : "unknown", - (thinkpad_id.ec_version_str) ? - thinkpad_id.ec_version_str : "unknown"); - - if (thinkpad_id.vendor && thinkpad_id.model_str) - printk(TPACPI_INFO "%s %s, model %s\n", - (thinkpad_id.vendor == PCI_VENDOR_ID_IBM) ? - "IBM" : ((thinkpad_id.vendor == - PCI_VENDOR_ID_LENOVO) ? - "Lenovo" : "Unknown vendor"), - thinkpad_id.model_str, - (thinkpad_id.nummodel_str) ? - thinkpad_id.nummodel_str : "unknown"); - - return 0; -} - -static int thinkpad_acpi_driver_read(char *p) -{ - int len = 0; - - len += sprintf(p + len, "driver:\t\t%s\n", TPACPI_DESC); - len += sprintf(p + len, "version:\t%s\n", TPACPI_VERSION); - - return len; -} - -static struct ibm_struct thinkpad_acpi_driver_data = { - .name = "driver", - .read = thinkpad_acpi_driver_read, -}; - -/************************************************************************* - * Hotkey subdriver - */ - -enum { /* hot key scan codes (derived from ACPI DSDT) */ - TP_ACPI_HOTKEYSCAN_FNF1 = 0, - TP_ACPI_HOTKEYSCAN_FNF2, - TP_ACPI_HOTKEYSCAN_FNF3, - TP_ACPI_HOTKEYSCAN_FNF4, - TP_ACPI_HOTKEYSCAN_FNF5, - TP_ACPI_HOTKEYSCAN_FNF6, - TP_ACPI_HOTKEYSCAN_FNF7, - TP_ACPI_HOTKEYSCAN_FNF8, - TP_ACPI_HOTKEYSCAN_FNF9, - TP_ACPI_HOTKEYSCAN_FNF10, - TP_ACPI_HOTKEYSCAN_FNF11, - TP_ACPI_HOTKEYSCAN_FNF12, - TP_ACPI_HOTKEYSCAN_FNBACKSPACE, - TP_ACPI_HOTKEYSCAN_FNINSERT, - TP_ACPI_HOTKEYSCAN_FNDELETE, - TP_ACPI_HOTKEYSCAN_FNHOME, - TP_ACPI_HOTKEYSCAN_FNEND, - TP_ACPI_HOTKEYSCAN_FNPAGEUP, - TP_ACPI_HOTKEYSCAN_FNPAGEDOWN, - TP_ACPI_HOTKEYSCAN_FNSPACE, - TP_ACPI_HOTKEYSCAN_VOLUMEUP, - TP_ACPI_HOTKEYSCAN_VOLUMEDOWN, - TP_ACPI_HOTKEYSCAN_MUTE, - TP_ACPI_HOTKEYSCAN_THINKPAD, -}; - -enum { /* Keys available through NVRAM polling */ - TPACPI_HKEY_NVRAM_KNOWN_MASK = 0x00fb88c0U, - TPACPI_HKEY_NVRAM_GOOD_MASK = 0x00fb8000U, -}; - -enum { /* Positions of some of the keys in hotkey masks */ - TP_ACPI_HKEY_DISPSWTCH_MASK = 1 << TP_ACPI_HOTKEYSCAN_FNF7, - TP_ACPI_HKEY_DISPXPAND_MASK = 1 << TP_ACPI_HOTKEYSCAN_FNF8, - TP_ACPI_HKEY_HIBERNATE_MASK = 1 << TP_ACPI_HOTKEYSCAN_FNF12, - TP_ACPI_HKEY_BRGHTUP_MASK = 1 << TP_ACPI_HOTKEYSCAN_FNHOME, - TP_ACPI_HKEY_BRGHTDWN_MASK = 1 << TP_ACPI_HOTKEYSCAN_FNEND, - TP_ACPI_HKEY_THNKLGHT_MASK = 1 << TP_ACPI_HOTKEYSCAN_FNPAGEUP, - TP_ACPI_HKEY_ZOOM_MASK = 1 << TP_ACPI_HOTKEYSCAN_FNSPACE, - TP_ACPI_HKEY_VOLUP_MASK = 1 << TP_ACPI_HOTKEYSCAN_VOLUMEUP, - TP_ACPI_HKEY_VOLDWN_MASK = 1 << TP_ACPI_HOTKEYSCAN_VOLUMEDOWN, - TP_ACPI_HKEY_MUTE_MASK = 1 << TP_ACPI_HOTKEYSCAN_MUTE, - TP_ACPI_HKEY_THINKPAD_MASK = 1 << TP_ACPI_HOTKEYSCAN_THINKPAD, -}; - -enum { /* NVRAM to ACPI HKEY group map */ - TP_NVRAM_HKEY_GROUP_HK2 = TP_ACPI_HKEY_THINKPAD_MASK | - TP_ACPI_HKEY_ZOOM_MASK | - TP_ACPI_HKEY_DISPSWTCH_MASK | - TP_ACPI_HKEY_HIBERNATE_MASK, - TP_NVRAM_HKEY_GROUP_BRIGHTNESS = TP_ACPI_HKEY_BRGHTUP_MASK | - TP_ACPI_HKEY_BRGHTDWN_MASK, - TP_NVRAM_HKEY_GROUP_VOLUME = TP_ACPI_HKEY_VOLUP_MASK | - TP_ACPI_HKEY_VOLDWN_MASK | - TP_ACPI_HKEY_MUTE_MASK, -}; - -#ifdef CONFIG_THINKPAD_ACPI_HOTKEY_POLL -struct tp_nvram_state { - u16 thinkpad_toggle:1; - u16 zoom_toggle:1; - u16 display_toggle:1; - u16 thinklight_toggle:1; - u16 hibernate_toggle:1; - u16 displayexp_toggle:1; - u16 display_state:1; - u16 brightness_toggle:1; - u16 volume_toggle:1; - u16 mute:1; - - u8 brightness_level; - u8 volume_level; -}; - -static struct task_struct *tpacpi_hotkey_task; -static u32 hotkey_source_mask; /* bit mask 0=ACPI,1=NVRAM */ -static int hotkey_poll_freq = 10; /* Hz */ -static struct mutex hotkey_thread_mutex; -static struct mutex hotkey_thread_data_mutex; -static unsigned int hotkey_config_change; - -#else /* CONFIG_THINKPAD_ACPI_HOTKEY_POLL */ - -#define hotkey_source_mask 0U - -#endif /* CONFIG_THINKPAD_ACPI_HOTKEY_POLL */ - -static struct mutex hotkey_mutex; - -static enum { /* Reasons for waking up */ - TP_ACPI_WAKEUP_NONE = 0, /* None or unknown */ - TP_ACPI_WAKEUP_BAYEJ, /* Bay ejection request */ - TP_ACPI_WAKEUP_UNDOCK, /* Undock request */ -} hotkey_wakeup_reason; - -static int hotkey_autosleep_ack; - -static int hotkey_orig_status; -static u32 hotkey_orig_mask; -static u32 hotkey_all_mask; -static u32 hotkey_reserved_mask; -static u32 hotkey_mask; - -static unsigned int hotkey_report_mode; - -static u16 *hotkey_keycode_map; - -static struct attribute_set *hotkey_dev_attributes; - -#ifdef CONFIG_THINKPAD_ACPI_HOTKEY_POLL -#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); -#else -#define HOTKEY_CONFIG_CRITICAL_START -#define HOTKEY_CONFIG_CRITICAL_END -#endif /* CONFIG_THINKPAD_ACPI_HOTKEY_POLL */ - -/* HKEY.MHKG() return bits */ -#define TP_HOTKEY_TABLET_MASK (1 << 3) - -static int hotkey_get_wlsw(int *status) -{ - if (!acpi_evalf(hkey_handle, status, "WLSW", "d")) - return -EIO; - return 0; -} - -static int hotkey_get_tablet_mode(int *status) -{ - int s; - - if (!acpi_evalf(hkey_handle, &s, "MHKG", "d")) - return -EIO; - - *status = ((s & TP_HOTKEY_TABLET_MASK) != 0); - return 0; -} - -/* - * Call with hotkey_ |