ia64/linux-2.6.18-xen.hg

diff drivers/acpi/button.c @ 0:831230e53067

Import 2.6.18 from kernel.org tarball.
author Ian Campbell <ian.campbell@xensource.com>
date Wed Apr 11 14:15:44 2007 +0100 (2007-04-11)
parents
children
line diff
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/drivers/acpi/button.c	Wed Apr 11 14:15:44 2007 +0100
     1.3 @@ -0,0 +1,469 @@
     1.4 +/*
     1.5 + *  acpi_button.c - ACPI Button Driver ($Revision: 30 $)
     1.6 + *
     1.7 + *  Copyright (C) 2001, 2002 Andy Grover <andrew.grover@intel.com>
     1.8 + *  Copyright (C) 2001, 2002 Paul Diefenbaugh <paul.s.diefenbaugh@intel.com>
     1.9 + *
    1.10 + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    1.11 + *
    1.12 + *  This program is free software; you can redistribute it and/or modify
    1.13 + *  it under the terms of the GNU General Public License as published by
    1.14 + *  the Free Software Foundation; either version 2 of the License, or (at
    1.15 + *  your option) any later version.
    1.16 + *
    1.17 + *  This program is distributed in the hope that it will be useful, but
    1.18 + *  WITHOUT ANY WARRANTY; without even the implied warranty of
    1.19 + *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    1.20 + *  General Public License for more details.
    1.21 + *
    1.22 + *  You should have received a copy of the GNU General Public License along
    1.23 + *  with this program; if not, write to the Free Software Foundation, Inc.,
    1.24 + *  59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
    1.25 + *
    1.26 + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    1.27 + */
    1.28 +
    1.29 +#include <linux/kernel.h>
    1.30 +#include <linux/module.h>
    1.31 +#include <linux/init.h>
    1.32 +#include <linux/types.h>
    1.33 +#include <linux/proc_fs.h>
    1.34 +#include <linux/seq_file.h>
    1.35 +#include <acpi/acpi_bus.h>
    1.36 +#include <acpi/acpi_drivers.h>
    1.37 +
    1.38 +#define ACPI_BUTTON_COMPONENT		0x00080000
    1.39 +#define ACPI_BUTTON_DRIVER_NAME		"ACPI Button Driver"
    1.40 +#define ACPI_BUTTON_CLASS		"button"
    1.41 +#define ACPI_BUTTON_FILE_INFO		"info"
    1.42 +#define ACPI_BUTTON_FILE_STATE		"state"
    1.43 +#define ACPI_BUTTON_TYPE_UNKNOWN	0x00
    1.44 +#define ACPI_BUTTON_NOTIFY_STATUS	0x80
    1.45 +
    1.46 +#define ACPI_BUTTON_SUBCLASS_POWER	"power"
    1.47 +#define ACPI_BUTTON_HID_POWER		"PNP0C0C"
    1.48 +#define ACPI_BUTTON_DEVICE_NAME_POWER	"Power Button (CM)"
    1.49 +#define ACPI_BUTTON_DEVICE_NAME_POWERF	"Power Button (FF)"
    1.50 +#define ACPI_BUTTON_TYPE_POWER		0x01
    1.51 +#define ACPI_BUTTON_TYPE_POWERF		0x02
    1.52 +
    1.53 +#define ACPI_BUTTON_SUBCLASS_SLEEP	"sleep"
    1.54 +#define ACPI_BUTTON_HID_SLEEP		"PNP0C0E"
    1.55 +#define ACPI_BUTTON_DEVICE_NAME_SLEEP	"Sleep Button (CM)"
    1.56 +#define ACPI_BUTTON_DEVICE_NAME_SLEEPF	"Sleep Button (FF)"
    1.57 +#define ACPI_BUTTON_TYPE_SLEEP		0x03
    1.58 +#define ACPI_BUTTON_TYPE_SLEEPF		0x04
    1.59 +
    1.60 +#define ACPI_BUTTON_SUBCLASS_LID	"lid"
    1.61 +#define ACPI_BUTTON_HID_LID		"PNP0C0D"
    1.62 +#define ACPI_BUTTON_DEVICE_NAME_LID	"Lid Switch"
    1.63 +#define ACPI_BUTTON_TYPE_LID		0x05
    1.64 +
    1.65 +#define _COMPONENT		ACPI_BUTTON_COMPONENT
    1.66 +ACPI_MODULE_NAME("acpi_button")
    1.67 +
    1.68 +    MODULE_AUTHOR("Paul Diefenbaugh");
    1.69 +MODULE_DESCRIPTION(ACPI_BUTTON_DRIVER_NAME);
    1.70 +MODULE_LICENSE("GPL");
    1.71 +
    1.72 +static int acpi_button_add(struct acpi_device *device);
    1.73 +static int acpi_button_remove(struct acpi_device *device, int type);
    1.74 +static int acpi_button_info_open_fs(struct inode *inode, struct file *file);
    1.75 +static int acpi_button_state_open_fs(struct inode *inode, struct file *file);
    1.76 +
    1.77 +static struct acpi_driver acpi_button_driver = {
    1.78 +	.name = ACPI_BUTTON_DRIVER_NAME,
    1.79 +	.class = ACPI_BUTTON_CLASS,
    1.80 +	.ids = "ACPI_FPB,ACPI_FSB,PNP0C0D,PNP0C0C,PNP0C0E",
    1.81 +	.ops = {
    1.82 +		.add = acpi_button_add,
    1.83 +		.remove = acpi_button_remove,
    1.84 +		},
    1.85 +};
    1.86 +
    1.87 +struct acpi_button {
    1.88 +	struct acpi_device *device;	/* Fixed button kludge */
    1.89 +	u8 type;
    1.90 +	unsigned long pushed;
    1.91 +};
    1.92 +
    1.93 +static const struct file_operations acpi_button_info_fops = {
    1.94 +	.open = acpi_button_info_open_fs,
    1.95 +	.read = seq_read,
    1.96 +	.llseek = seq_lseek,
    1.97 +	.release = single_release,
    1.98 +};
    1.99 +
   1.100 +static const struct file_operations acpi_button_state_fops = {
   1.101 +	.open = acpi_button_state_open_fs,
   1.102 +	.read = seq_read,
   1.103 +	.llseek = seq_lseek,
   1.104 +	.release = single_release,
   1.105 +};
   1.106 +
   1.107 +/* --------------------------------------------------------------------------
   1.108 +                              FS Interface (/proc)
   1.109 +   -------------------------------------------------------------------------- */
   1.110 +
   1.111 +static struct proc_dir_entry *acpi_button_dir;
   1.112 +
   1.113 +static int acpi_button_info_seq_show(struct seq_file *seq, void *offset)
   1.114 +{
   1.115 +	struct acpi_button *button = (struct acpi_button *)seq->private;
   1.116 +
   1.117 +
   1.118 +	if (!button || !button->device)
   1.119 +		return 0;
   1.120 +
   1.121 +	seq_printf(seq, "type:                    %s\n",
   1.122 +		   acpi_device_name(button->device));
   1.123 +
   1.124 +	return 0;
   1.125 +}
   1.126 +
   1.127 +static int acpi_button_info_open_fs(struct inode *inode, struct file *file)
   1.128 +{
   1.129 +	return single_open(file, acpi_button_info_seq_show, PDE(inode)->data);
   1.130 +}
   1.131 +
   1.132 +static int acpi_button_state_seq_show(struct seq_file *seq, void *offset)
   1.133 +{
   1.134 +	struct acpi_button *button = (struct acpi_button *)seq->private;
   1.135 +	acpi_status status;
   1.136 +	unsigned long state;
   1.137 +
   1.138 +
   1.139 +	if (!button || !button->device)
   1.140 +		return 0;
   1.141 +
   1.142 +	status = acpi_evaluate_integer(button->device->handle, "_LID", NULL, &state);
   1.143 +	if (ACPI_FAILURE(status)) {
   1.144 +		seq_printf(seq, "state:      unsupported\n");
   1.145 +	} else {
   1.146 +		seq_printf(seq, "state:      %s\n",
   1.147 +			   (state ? "open" : "closed"));
   1.148 +	}
   1.149 +
   1.150 +	return 0;
   1.151 +}
   1.152 +
   1.153 +static int acpi_button_state_open_fs(struct inode *inode, struct file *file)
   1.154 +{
   1.155 +	return single_open(file, acpi_button_state_seq_show, PDE(inode)->data);
   1.156 +}
   1.157 +
   1.158 +static struct proc_dir_entry *acpi_power_dir;
   1.159 +static struct proc_dir_entry *acpi_sleep_dir;
   1.160 +static struct proc_dir_entry *acpi_lid_dir;
   1.161 +
   1.162 +static int acpi_button_add_fs(struct acpi_device *device)
   1.163 +{
   1.164 +	struct proc_dir_entry *entry = NULL;
   1.165 +	struct acpi_button *button = NULL;
   1.166 +
   1.167 +
   1.168 +	if (!device || !acpi_driver_data(device))
   1.169 +		return -EINVAL;
   1.170 +
   1.171 +	button = acpi_driver_data(device);
   1.172 +
   1.173 +	switch (button->type) {
   1.174 +	case ACPI_BUTTON_TYPE_POWER:
   1.175 +	case ACPI_BUTTON_TYPE_POWERF:
   1.176 +		if (!acpi_power_dir)
   1.177 +			acpi_power_dir = proc_mkdir(ACPI_BUTTON_SUBCLASS_POWER,
   1.178 +						    acpi_button_dir);
   1.179 +		entry = acpi_power_dir;
   1.180 +		break;
   1.181 +	case ACPI_BUTTON_TYPE_SLEEP:
   1.182 +	case ACPI_BUTTON_TYPE_SLEEPF:
   1.183 +		if (!acpi_sleep_dir)
   1.184 +			acpi_sleep_dir = proc_mkdir(ACPI_BUTTON_SUBCLASS_SLEEP,
   1.185 +						    acpi_button_dir);
   1.186 +		entry = acpi_sleep_dir;
   1.187 +		break;
   1.188 +	case ACPI_BUTTON_TYPE_LID:
   1.189 +		if (!acpi_lid_dir)
   1.190 +			acpi_lid_dir = proc_mkdir(ACPI_BUTTON_SUBCLASS_LID,
   1.191 +						  acpi_button_dir);
   1.192 +		entry = acpi_lid_dir;
   1.193 +		break;
   1.194 +	}
   1.195 +
   1.196 +	if (!entry)
   1.197 +		return -ENODEV;
   1.198 +	entry->owner = THIS_MODULE;
   1.199 +
   1.200 +	acpi_device_dir(device) = proc_mkdir(acpi_device_bid(device), entry);
   1.201 +	if (!acpi_device_dir(device))
   1.202 +		return -ENODEV;
   1.203 +	acpi_device_dir(device)->owner = THIS_MODULE;
   1.204 +
   1.205 +	/* 'info' [R] */
   1.206 +	entry = create_proc_entry(ACPI_BUTTON_FILE_INFO,
   1.207 +				  S_IRUGO, acpi_device_dir(device));
   1.208 +	if (!entry)
   1.209 +		return -ENODEV;
   1.210 +	else {
   1.211 +		entry->proc_fops = &acpi_button_info_fops;
   1.212 +		entry->data = acpi_driver_data(device);
   1.213 +		entry->owner = THIS_MODULE;
   1.214 +	}
   1.215 +
   1.216 +	/* show lid state [R] */
   1.217 +	if (button->type == ACPI_BUTTON_TYPE_LID) {
   1.218 +		entry = create_proc_entry(ACPI_BUTTON_FILE_STATE,
   1.219 +					  S_IRUGO, acpi_device_dir(device));
   1.220 +		if (!entry)
   1.221 +			return -ENODEV;
   1.222 +		else {
   1.223 +			entry->proc_fops = &acpi_button_state_fops;
   1.224 +			entry->data = acpi_driver_data(device);
   1.225 +			entry->owner = THIS_MODULE;
   1.226 +		}
   1.227 +	}
   1.228 +
   1.229 +	return 0;
   1.230 +}
   1.231 +
   1.232 +static int acpi_button_remove_fs(struct acpi_device *device)
   1.233 +{
   1.234 +	struct acpi_button *button = NULL;
   1.235 +
   1.236 +
   1.237 +	button = acpi_driver_data(device);
   1.238 +	if (acpi_device_dir(device)) {
   1.239 +		if (button->type == ACPI_BUTTON_TYPE_LID)
   1.240 +			remove_proc_entry(ACPI_BUTTON_FILE_STATE,
   1.241 +					  acpi_device_dir(device));
   1.242 +		remove_proc_entry(ACPI_BUTTON_FILE_INFO,
   1.243 +				  acpi_device_dir(device));
   1.244 +
   1.245 +		remove_proc_entry(acpi_device_bid(device),
   1.246 +				  acpi_device_dir(device)->parent);
   1.247 +		acpi_device_dir(device) = NULL;
   1.248 +	}
   1.249 +
   1.250 +	return 0;
   1.251 +}
   1.252 +
   1.253 +/* --------------------------------------------------------------------------
   1.254 +                                Driver Interface
   1.255 +   -------------------------------------------------------------------------- */
   1.256 +
   1.257 +static void acpi_button_notify(acpi_handle handle, u32 event, void *data)
   1.258 +{
   1.259 +	struct acpi_button *button = (struct acpi_button *)data;
   1.260 +
   1.261 +
   1.262 +	if (!button || !button->device)
   1.263 +		return;
   1.264 +
   1.265 +	switch (event) {
   1.266 +	case ACPI_BUTTON_NOTIFY_STATUS:
   1.267 +		acpi_bus_generate_event(button->device, event,
   1.268 +					++button->pushed);
   1.269 +		break;
   1.270 +	default:
   1.271 +		ACPI_DEBUG_PRINT((ACPI_DB_INFO,
   1.272 +				  "Unsupported event [0x%x]\n", event));
   1.273 +		break;
   1.274 +	}
   1.275 +
   1.276 +	return;
   1.277 +}
   1.278 +
   1.279 +static acpi_status acpi_button_notify_fixed(void *data)
   1.280 +{
   1.281 +	struct acpi_button *button = (struct acpi_button *)data;
   1.282 +
   1.283 +
   1.284 +	if (!button)
   1.285 +		return AE_BAD_PARAMETER;
   1.286 +
   1.287 +	acpi_button_notify(button->device->handle, ACPI_BUTTON_NOTIFY_STATUS, button);
   1.288 +
   1.289 +	return AE_OK;
   1.290 +}
   1.291 +
   1.292 +static int acpi_button_add(struct acpi_device *device)
   1.293 +{
   1.294 +	int result = 0;
   1.295 +	acpi_status status = AE_OK;
   1.296 +	struct acpi_button *button = NULL;
   1.297 +
   1.298 +
   1.299 +	if (!device)
   1.300 +		return -EINVAL;
   1.301 +
   1.302 +	button = kmalloc(sizeof(struct acpi_button), GFP_KERNEL);
   1.303 +	if (!button)
   1.304 +		return -ENOMEM;
   1.305 +	memset(button, 0, sizeof(struct acpi_button));
   1.306 +
   1.307 +	button->device = device;
   1.308 +	acpi_driver_data(device) = button;
   1.309 +
   1.310 +	/*
   1.311 +	 * Determine the button type (via hid), as fixed-feature buttons
   1.312 +	 * need to be handled a bit differently than generic-space.
   1.313 +	 */
   1.314 +	if (!strcmp(acpi_device_hid(device), ACPI_BUTTON_HID_POWER)) {
   1.315 +		button->type = ACPI_BUTTON_TYPE_POWER;
   1.316 +		strcpy(acpi_device_name(device), ACPI_BUTTON_DEVICE_NAME_POWER);
   1.317 +		sprintf(acpi_device_class(device), "%s/%s",
   1.318 +			ACPI_BUTTON_CLASS, ACPI_BUTTON_SUBCLASS_POWER);
   1.319 +	} else if (!strcmp(acpi_device_hid(device), ACPI_BUTTON_HID_POWERF)) {
   1.320 +		button->type = ACPI_BUTTON_TYPE_POWERF;
   1.321 +		strcpy(acpi_device_name(device),
   1.322 +		       ACPI_BUTTON_DEVICE_NAME_POWERF);
   1.323 +		sprintf(acpi_device_class(device), "%s/%s",
   1.324 +			ACPI_BUTTON_CLASS, ACPI_BUTTON_SUBCLASS_POWER);
   1.325 +	} else if (!strcmp(acpi_device_hid(device), ACPI_BUTTON_HID_SLEEP)) {
   1.326 +		button->type = ACPI_BUTTON_TYPE_SLEEP;
   1.327 +		strcpy(acpi_device_name(device), ACPI_BUTTON_DEVICE_NAME_SLEEP);
   1.328 +		sprintf(acpi_device_class(device), "%s/%s",
   1.329 +			ACPI_BUTTON_CLASS, ACPI_BUTTON_SUBCLASS_SLEEP);
   1.330 +	} else if (!strcmp(acpi_device_hid(device), ACPI_BUTTON_HID_SLEEPF)) {
   1.331 +		button->type = ACPI_BUTTON_TYPE_SLEEPF;
   1.332 +		strcpy(acpi_device_name(device),
   1.333 +		       ACPI_BUTTON_DEVICE_NAME_SLEEPF);
   1.334 +		sprintf(acpi_device_class(device), "%s/%s",
   1.335 +			ACPI_BUTTON_CLASS, ACPI_BUTTON_SUBCLASS_SLEEP);
   1.336 +	} else if (!strcmp(acpi_device_hid(device), ACPI_BUTTON_HID_LID)) {
   1.337 +		button->type = ACPI_BUTTON_TYPE_LID;
   1.338 +		strcpy(acpi_device_name(device), ACPI_BUTTON_DEVICE_NAME_LID);
   1.339 +		sprintf(acpi_device_class(device), "%s/%s",
   1.340 +			ACPI_BUTTON_CLASS, ACPI_BUTTON_SUBCLASS_LID);
   1.341 +	} else {
   1.342 +		printk(KERN_ERR PREFIX "Unsupported hid [%s]\n",
   1.343 +			    acpi_device_hid(device));
   1.344 +		result = -ENODEV;
   1.345 +		goto end;
   1.346 +	}
   1.347 +
   1.348 +	result = acpi_button_add_fs(device);
   1.349 +	if (result)
   1.350 +		goto end;
   1.351 +
   1.352 +	switch (button->type) {
   1.353 +	case ACPI_BUTTON_TYPE_POWERF:
   1.354 +		status =
   1.355 +		    acpi_install_fixed_event_handler(ACPI_EVENT_POWER_BUTTON,
   1.356 +						     acpi_button_notify_fixed,
   1.357 +						     button);
   1.358 +		break;
   1.359 +	case ACPI_BUTTON_TYPE_SLEEPF:
   1.360 +		status =
   1.361 +		    acpi_install_fixed_event_handler(ACPI_EVENT_SLEEP_BUTTON,
   1.362 +						     acpi_button_notify_fixed,
   1.363 +						     button);
   1.364 +		break;
   1.365 +	default:
   1.366 +		status = acpi_install_notify_handler(device->handle,
   1.367 +						     ACPI_DEVICE_NOTIFY,
   1.368 +						     acpi_button_notify,
   1.369 +						     button);
   1.370 +		break;
   1.371 +	}
   1.372 +
   1.373 +	if (ACPI_FAILURE(status)) {
   1.374 +		result = -ENODEV;
   1.375 +		goto end;
   1.376 +	}
   1.377 +
   1.378 +	if (device->wakeup.flags.valid) {
   1.379 +		/* Button's GPE is run-wake GPE */
   1.380 +		acpi_set_gpe_type(device->wakeup.gpe_device,
   1.381 +				  device->wakeup.gpe_number,
   1.382 +				  ACPI_GPE_TYPE_WAKE_RUN);
   1.383 +		acpi_enable_gpe(device->wakeup.gpe_device,
   1.384 +				device->wakeup.gpe_number, ACPI_NOT_ISR);
   1.385 +		device->wakeup.state.enabled = 1;
   1.386 +	}
   1.387 +
   1.388 +	printk(KERN_INFO PREFIX "%s [%s]\n",
   1.389 +	       acpi_device_name(device), acpi_device_bid(device));
   1.390 +
   1.391 +      end:
   1.392 +	if (result) {
   1.393 +		acpi_button_remove_fs(device);
   1.394 +		kfree(button);
   1.395 +	}
   1.396 +
   1.397 +	return result;
   1.398 +}
   1.399 +
   1.400 +static int acpi_button_remove(struct acpi_device *device, int type)
   1.401 +{
   1.402 +	acpi_status status = 0;
   1.403 +	struct acpi_button *button = NULL;
   1.404 +
   1.405 +
   1.406 +	if (!device || !acpi_driver_data(device))
   1.407 +		return -EINVAL;
   1.408 +
   1.409 +	button = acpi_driver_data(device);
   1.410 +
   1.411 +	/* Unregister for device notifications. */
   1.412 +	switch (button->type) {
   1.413 +	case ACPI_BUTTON_TYPE_POWERF:
   1.414 +		status =
   1.415 +		    acpi_remove_fixed_event_handler(ACPI_EVENT_POWER_BUTTON,
   1.416 +						    acpi_button_notify_fixed);
   1.417 +		break;
   1.418 +	case ACPI_BUTTON_TYPE_SLEEPF:
   1.419 +		status =
   1.420 +		    acpi_remove_fixed_event_handler(ACPI_EVENT_SLEEP_BUTTON,
   1.421 +						    acpi_button_notify_fixed);
   1.422 +		break;
   1.423 +	default:
   1.424 +		status = acpi_remove_notify_handler(device->handle,
   1.425 +						    ACPI_DEVICE_NOTIFY,
   1.426 +						    acpi_button_notify);
   1.427 +		break;
   1.428 +	}
   1.429 +
   1.430 +	acpi_button_remove_fs(device);
   1.431 +
   1.432 +	kfree(button);
   1.433 +
   1.434 +	return 0;
   1.435 +}
   1.436 +
   1.437 +static int __init acpi_button_init(void)
   1.438 +{
   1.439 +	int result = 0;
   1.440 +
   1.441 +
   1.442 +	acpi_button_dir = proc_mkdir(ACPI_BUTTON_CLASS, acpi_root_dir);
   1.443 +	if (!acpi_button_dir)
   1.444 +		return -ENODEV;
   1.445 +	acpi_button_dir->owner = THIS_MODULE;
   1.446 +	result = acpi_bus_register_driver(&acpi_button_driver);
   1.447 +	if (result < 0) {
   1.448 +		remove_proc_entry(ACPI_BUTTON_CLASS, acpi_root_dir);
   1.449 +		return -ENODEV;
   1.450 +	}
   1.451 +
   1.452 +	return 0;
   1.453 +}
   1.454 +
   1.455 +static void __exit acpi_button_exit(void)
   1.456 +{
   1.457 +
   1.458 +	acpi_bus_unregister_driver(&acpi_button_driver);
   1.459 +
   1.460 +	if (acpi_power_dir)
   1.461 +		remove_proc_entry(ACPI_BUTTON_SUBCLASS_POWER, acpi_button_dir);
   1.462 +	if (acpi_sleep_dir)
   1.463 +		remove_proc_entry(ACPI_BUTTON_SUBCLASS_SLEEP, acpi_button_dir);
   1.464 +	if (acpi_lid_dir)
   1.465 +		remove_proc_entry(ACPI_BUTTON_SUBCLASS_LID, acpi_button_dir);
   1.466 +	remove_proc_entry(ACPI_BUTTON_CLASS, acpi_root_dir);
   1.467 +
   1.468 +	return;
   1.469 +}
   1.470 +
   1.471 +module_init(acpi_button_init);
   1.472 +module_exit(acpi_button_exit);