/* * leds-hp-disk.c - driver for HP "hard disk protection" LED * * Copyright (C) 2008 Pavel Machek * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include <linux/kernel.h> #include <linux/init.h> #include <linux/dmi.h> #include <linux/module.h> #include <linux/types.h> #include <linux/platform_device.h> #include <linux/interrupt.h> #include <linux/input.h> #include <linux/kthread.h> #include <linux/version.h> #include <linux/leds.h> #include <acpi/acpi_drivers.h> #define DRIVER_NAME "leds-hp-disk" #define ACPI_MDPS_CLASS "led" /* For automatic insertion of the module */ static struct acpi_device_id hpled_device_ids[] = { {"HPQ0004", 0}, /* HP Mobile Data Protection System PNP */ {"", 0}, }; MODULE_DEVICE_TABLE(acpi, hpled_device_ids); struct acpi_hpled { struct acpi_device *device; /* The ACPI device */ }; static struct acpi_hpled adev; static acpi_status hpled_acpi_write(acpi_handle handle, int reg) { unsigned long long ret; /* Not used when writing */ union acpi_object in_obj[1]; struct acpi_object_list args = { 1, in_obj }; in_obj[0].type = ACPI_TYPE_INTEGER; in_obj[0].integer.value = reg; return acpi_evaluate_integer(handle, "ALED", &args, &ret); } static void hpled_set(struct led_classdev *led_cdev, enum led_brightness value) { hpled_acpi_write(adev.device->handle, !!value); } static struct led_classdev hpled_led = { .name = "hp:red:hddprotection", .default_trigger = "heartbeat", .brightness_set = hpled_set, }; #ifdef CONFIG_PM static int hpled_suspend(struct acpi_device *dev, pm_message_t state) { led_classdev_suspend(&hpled_led); return 0; } static int hpled_resume(struct acpi_device *dev) { led_classdev_resume(&hpled_led); return 0; } #else #define hpled_suspend NULL #define hpled_resume NULL #endif static int hpled_add(struct acpi_device *device) { int ret; if (!device) return -EINVAL; adev.device = device; strcpy(acpi_device_name(device), DRIVER_NAME); strcpy(acpi_device_class(device), ACPI_MDPS_CLASS); device->driver_data = &adev; ret = led_classdev_register(NULL, &hpled_led); return ret; } static int hpled_remove(struct acpi_device *device, int type) { if (!device) return -EINVAL; led_classdev_unregister(&hpled_led); return 0; } static struct acpi_driver leds_hp_driver = { .name = DRIVER_NAME, .class = ACPI_MDPS_CLASS, .ids = hpled_device_ids, .ops = { .add = hpled_add, .remove = hpled_remove, .suspend = hpled_suspend, .resume = hpled_resume, } }; static int __init hpled_init_module(void) { int ret; if (acpi_disabled) return -ENODEV; ret = acpi_bus_register_driver(&leds_hp_driver); if (ret < 0) return ret; printk(KERN_INFO DRIVER_NAME " driver loaded.\n"); return 0; } static void __exit hpled_exit_module(void) { acpi_bus_unregister_driver(&leds_hp_driver); } MODULE_DESCRIPTION("Driver for HP disk protection LED"); MODULE_AUTHOR("Pavel Machek <pavel@suse.cz>"); MODULE_LICENSE("GPL"); module_init(hpled_init_module); module_exit(hpled_exit_module);