thinkpad_acpi.c

来自「linux 内核源代码」· C语言 代码 · 共 2,636 行 · 第 1/5 页

C
2,636
字号
/* *  thinkpad_acpi.c - ThinkPad ACPI Extras * * *  Copyright (C) 2004-2005 Borislav Deianov <borislav@users.sf.net> *  Copyright (C) 2006-2007 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 IBM_VERSION "0.17"#define TPACPI_SYSFS_VERSION 0x020000/* *  Changelog: *  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-08-17  0.12	fix compilation on 2.6.13-rc kernels *  2005-03-17	0.11	support for 600e, 770x *			    thanks to Jamie Lentin <lentinj@dial.pipex.com> *			support for 770e, G41 *			G40 and G41 don't have a thinklight *			temperatures no longer experimental *			experimental brightness control *			experimental volume control *			experimental fan enable/disable *  2005-01-16	0.10	fix module loading on R30, R31 *  2005-01-16	0.9	support for 570, R30, R31 *			ultrabay support on A22p, A3x *			limit arg for cmos, led, beep, drop experimental status *			more capable led control on A21e, A22p, T20-22, X20 *			experimental temperatures and fan speed *			experimental embedded controller register dump *			mark more functions as __init, drop incorrect __exit *			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> *  2004-10-23	0.7	fix module loading on A21e, A22p, T20, T21, X20 *			fix led control on A21e *  2004-10-19	0.6	use acpi_bus_register_driver() to claim HKEY device *  2004-10-18	0.5	thinklight support on A21e, G40, R32, T20, T21, X20 *			proc file format changed *			video_switch command *			experimental cmos control *			experimental led control *			experimental acpi sounds *  2004-09-16	0.4	support for module parameters *			hotkey mask can be prefixed by 0x *			video output switching *			video expansion control *			ultrabay eject support *			removed lcd brightness/on/off control, didn't work *  2004-08-17	0.3	support for R40 *			lcd off, brightness control *			thinklight on/off *  2004-08-14	0.2	support for T series, X20 *			bluetooth enable/disable *			hotkey events disabled by default *			removed fan control, currently useless *  2004-08-09	0.1	initial release, support for X series */#include "thinkpad_acpi.h"MODULE_AUTHOR("Borislav Deianov, Henrique de Moraes Holschuh");MODULE_DESCRIPTION(IBM_DESC);MODULE_VERSION(IBM_VERSION);MODULE_LICENSE("GPL");/* Please remove this in year 2009 */MODULE_ALIAS("ibm_acpi");/* * DMI matching for module autoloading * * See http://thinkwiki.org/wiki/List_of_DMI_IDs * See http://thinkwiki.org/wiki/BIOS_Upgrade_Downloads * * Only models listed in thinkwiki will be supported, so add yours * if it is not there yet. */#define IBM_BIOS_MODULE_ALIAS(__type) \	MODULE_ALIAS("dmi:bvnIBM:bvr" __type "ET??WW")/* Non-ancient thinkpads */MODULE_ALIAS("dmi:bvnIBM:*:svnIBM:*:pvrThinkPad*:rvnIBM:*");MODULE_ALIAS("dmi:bvnLENOVO:*:svnLENOVO:*:pvrThinkPad*:rvnLENOVO:*");/* Ancient thinkpad BIOSes have to be identified by * BIOS type or model number, and there are far less * BIOS types than model numbers... */IBM_BIOS_MODULE_ALIAS("I[B,D,H,I,M,N,O,T,W,V,Y,Z]");IBM_BIOS_MODULE_ALIAS("1[0,3,6,8,A-G,I,K,M-P,S,T]");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;/**************************************************************************** **************************************************************************** * * ACPI Helpers and device model * **************************************************************************** ****************************************************************************//************************************************************************* * ACPI basic handles */static acpi_handle root_handle;#define IBM_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 }IBM_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 */	   );IBM_HANDLE(ecrd, ec, "ECRD");	/* 570 */IBM_HANDLE(ecwr, ec, "ECWR");	/* 570 *//************************************************************************* * Misc ACPI handles */IBM_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 */IBM_HANDLE(hkey, ec, "\\_SB.HKEY",	/* 600e/x, 770e, 770x */	   "^HKEY",		/* R30, R31 */	   "HKEY",		/* all others */	   );			/* 570 *//************************************************************************* * 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[IBM_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(IBM_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(IBM_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, &params, 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(IBM_ERR "acpi_evalf() called "		       "with invalid format character '%c'\n", res_type);		return 0;	}	if (!success && !quiet)		printk(IBM_ERR "acpi_evalf(%s, %s, ...) failed: %d\n",		       method, fmt0, status);	return success;}static void __unused acpi_print_int(acpi_handle handle, char *method){	int i;	if (acpi_evalf(handle, &i, method, "d"))		printk(IBM_INFO "%s = 0x%x\n", method, i);	else		printk(IBM_ERR "error calling %s\n", method);}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;}static int _sta(acpi_handle handle){	int status;	if (!handle || !acpi_evalf(handle, &status, "_STA", "d"))		status = 0;	return status;}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 */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(IBM_ERR "acpi_bus_get_device(%s) failed: %d\n",			ibm->name, rc);		return -ENODEV;	}	acpi_driver_data(ibm->acpi->device) = ibm;	sprintf(acpi_device_class(ibm->acpi->device), "%s/%s",		IBM_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(IBM_NOTICE "another device driver is already handling %s events\n",				ibm->name);		} else {			printk(IBM_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(IBM_ERR "kzalloc(ibm->driver) failed\n");		return -ENOMEM;	}	sprintf(ibm->acpi->driver->name, "%s_%s", IBM_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(IBM_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;

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?