1 /* 2 * acpi_button.c - ACPI Button Driver ($Revision: 30 $) 3 * 4 * Copyright (C) 2001, 2002 Andy Grover <andrew.grover@intel.com> 5 * Copyright (C) 2001, 2002 Paul Diefenbaugh <paul.s.diefenbaugh@intel.com> 6 * 7 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 8 * 9 * This program is free software; you can redistribute it and/or modify 10 * it under the terms of the GNU General Public License as published by 11 * the Free Software Foundation; either version 2 of the License, or (at 12 * your option) any later version. 13 * 14 * This program is distributed in the hope that it will be useful, but 15 * WITHOUT ANY WARRANTY; without even the implied warranty of 16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 17 * General Public License for more details. 18 * 19 * You should have received a copy of the GNU General Public License along 20 * with this program; if not, write to the Free Software Foundation, Inc., 21 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. 22 * 23 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 24 */ 25 26 #include <linux/kernel.h> 27 #include <linux/module.h> 28 #include <linux/init.h> 29 #include <acpi/acpi_bus.h> 30 #include <acpi/acpi_drivers.h> 31 32 33 #define ACPI_BUTTON_COMPONENT 0x00080000 34 #define ACPI_BUTTON_DRIVER_NAME "ACPI Button Driver" 35 #define ACPI_BUTTON_CLASS "button" 36 #define ACPI_BUTTON_NOTIFY_STATUS 0x80 37 38 #define ACPI_BUTTON_SUBCLASS_POWER "power" 39 #define ACPI_BUTTON_HID_POWER "PNP0C0C" 40 #define ACPI_BUTTON_DEVICE_NAME_POWER "Power Button (CM)" 41 #define ACPI_BUTTON_DEVICE_NAME_POWERF "Power Button (FF)" 42 #define ACPI_BUTTON_TYPE_POWER 0x01 43 #define ACPI_BUTTON_TYPE_POWERF 0x02 44 45 #define ACPI_BUTTON_SUBCLASS_SLEEP "sleep" 46 #define ACPI_BUTTON_HID_SLEEP "PNP0C0E" 47 #define ACPI_BUTTON_DEVICE_NAME_SLEEP "Sleep Button (CM)" 48 #define ACPI_BUTTON_DEVICE_NAME_SLEEPF "Sleep Button (FF)" 49 #define ACPI_BUTTON_TYPE_SLEEP 0x03 50 #define ACPI_BUTTON_TYPE_SLEEPF 0x04 51 52 #define ACPI_BUTTON_SUBCLASS_LID "lid" 53 #define ACPI_BUTTON_HID_LID "PNP0C0D" 54 #define ACPI_BUTTON_DEVICE_NAME_LID "Lid Switch" 55 #define ACPI_BUTTON_TYPE_LID 0x05 56 57 #define _COMPONENT ACPI_BUTTON_COMPONENT 58 ACPI_MODULE_NAME ("acpi_button") 59 60 MODULE_AUTHOR("Paul Diefenbaugh"); 61 MODULE_DESCRIPTION(ACPI_BUTTON_DRIVER_NAME); 62 MODULE_LICENSE("GPL"); 63 64 65 static int acpi_button_add (struct acpi_device *device); 66 static int acpi_button_remove (struct acpi_device *device, int type); 67 68 static struct acpi_driver acpi_button_driver = { 69 .name = ACPI_BUTTON_DRIVER_NAME, 70 .class = ACPI_BUTTON_CLASS, 71 .ids = "ACPI_FPB,ACPI_FSB,PNP0C0D,PNP0C0C,PNP0C0E", 72 .ops = { 73 .add = acpi_button_add, 74 .remove = acpi_button_remove, 75 }, 76 }; 77 78 struct acpi_button { 79 acpi_handle handle; 80 struct acpi_device *device; /* Fixed button kludge */ 81 u8 type; 82 unsigned long pushed; 83 }; 84 85 /* -------------------------------------------------------------------------- 86 Driver Interface 87 -------------------------------------------------------------------------- */ 88 89 static void 90 acpi_button_notify ( 91 acpi_handle handle, 92 u32 event, 93 void *data) 94 { 95 struct acpi_button *button = (struct acpi_button *) data; 96 97 ACPI_FUNCTION_TRACE("acpi_button_notify"); 98 99 if (!button || !button->device) 100 return_VOID; 101 102 switch (event) { 103 case ACPI_BUTTON_NOTIFY_STATUS: 104 acpi_bus_generate_event(button->device, event, ++button->pushed); 105 break; 106 default: 107 ACPI_DEBUG_PRINT((ACPI_DB_INFO, 108 "Unsupported event [0x%x]\n", event)); 109 break; 110 } 111 112 return_VOID; 113 } 114 115 116 static acpi_status 117 acpi_button_notify_fixed ( 118 void *data) 119 { 120 struct acpi_button *button = (struct acpi_button *) data; 121 122 ACPI_FUNCTION_TRACE("acpi_button_notify_fixed"); 123 124 BUG_ON(!button); 125 126 acpi_button_notify(button->handle, ACPI_BUTTON_NOTIFY_STATUS, button); 127 128 return_ACPI_STATUS(AE_OK); 129 } 130 131 132 static int 133 acpi_button_add ( 134 struct acpi_device *device) 135 { 136 int result = 0; 137 acpi_status status = AE_OK; 138 struct acpi_button *button = NULL; 139 140 ACPI_FUNCTION_TRACE("acpi_button_add"); 141 142 if (!device) 143 return_VALUE(-EINVAL); 144 145 button = kmalloc(sizeof(struct acpi_button), GFP_KERNEL); 146 if (!button) 147 return_VALUE(-ENOMEM); 148 memset(button, 0, sizeof(struct acpi_button)); 149 150 button->device = device; 151 button->handle = device->handle; 152 acpi_driver_data(device) = button; 153 154 /* 155 * Determine the button type (via hid), as fixed-feature buttons 156 * need to be handled a bit differently than generic-space. 157 */ 158 if (!strcmp(acpi_device_hid(device), ACPI_BUTTON_HID_POWER)) { 159 button->type = ACPI_BUTTON_TYPE_POWER; 160 strcpy(acpi_device_name(device), 161 ACPI_BUTTON_DEVICE_NAME_POWER); 162 sprintf(acpi_device_class(device), "%s/%s", 163 ACPI_BUTTON_CLASS, ACPI_BUTTON_SUBCLASS_POWER); 164 } 165 else if (!strcmp(acpi_device_hid(device), ACPI_BUTTON_HID_POWERF)) { 166 button->type = ACPI_BUTTON_TYPE_POWERF; 167 strcpy(acpi_device_name(device), 168 ACPI_BUTTON_DEVICE_NAME_POWERF); 169 sprintf(acpi_device_class(device), "%s/%s", 170 ACPI_BUTTON_CLASS, ACPI_BUTTON_SUBCLASS_POWER); 171 } 172 else if (!strcmp(acpi_device_hid(device), ACPI_BUTTON_HID_SLEEP)) { 173 button->type = ACPI_BUTTON_TYPE_SLEEP; 174 strcpy(acpi_device_name(device), 175 ACPI_BUTTON_DEVICE_NAME_SLEEP); 176 sprintf(acpi_device_class(device), "%s/%s", 177 ACPI_BUTTON_CLASS, ACPI_BUTTON_SUBCLASS_SLEEP); 178 } 179 else if (!strcmp(acpi_device_hid(device), ACPI_BUTTON_HID_SLEEPF)) { 180 button->type = ACPI_BUTTON_TYPE_SLEEPF; 181 strcpy(acpi_device_name(device), 182 ACPI_BUTTON_DEVICE_NAME_SLEEPF); 183 sprintf(acpi_device_class(device), "%s/%s", 184 ACPI_BUTTON_CLASS, ACPI_BUTTON_SUBCLASS_SLEEP); 185 } 186 else if (!strcmp(acpi_device_hid(device), ACPI_BUTTON_HID_LID)) { 187 button->type = ACPI_BUTTON_TYPE_LID; 188 strcpy(acpi_device_name(device), 189 ACPI_BUTTON_DEVICE_NAME_LID); 190 sprintf(acpi_device_class(device), "%s/%s", 191 ACPI_BUTTON_CLASS, ACPI_BUTTON_SUBCLASS_LID); 192 } 193 else { 194 ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Unsupported hid [%s]\n", 195 acpi_device_hid(device))); 196 result = -ENODEV; 197 goto end; 198 } 199 200 switch (button->type) { 201 case ACPI_BUTTON_TYPE_POWERF: 202 status = acpi_install_fixed_event_handler ( 203 ACPI_EVENT_POWER_BUTTON, 204 acpi_button_notify_fixed, 205 button); 206 break; 207 case ACPI_BUTTON_TYPE_SLEEPF: 208 status = acpi_install_fixed_event_handler ( 209 ACPI_EVENT_SLEEP_BUTTON, 210 acpi_button_notify_fixed, 211 button); 212 break; 213 default: 214 status = acpi_install_notify_handler ( 215 button->handle, 216 ACPI_DEVICE_NOTIFY, 217 acpi_button_notify, 218 button); 219 break; 220 } 221 222 if (ACPI_FAILURE(status)) { 223 ACPI_DEBUG_PRINT((ACPI_DB_ERROR, 224 "Error installing notify handler\n")); 225 result = -ENODEV; 226 goto end; 227 } 228 229 if (device->wakeup.flags.valid) { 230 /* Button's GPE is run-wake GPE */ 231 acpi_set_gpe_type(device->wakeup.gpe_device, 232 device->wakeup.gpe_number, ACPI_GPE_TYPE_WAKE_RUN); 233 acpi_enable_gpe(device->wakeup.gpe_device, 234 device->wakeup.gpe_number, ACPI_NOT_ISR); 235 device->wakeup.state.enabled = 1; 236 } 237 238 printk(KERN_INFO PREFIX "%s [%s]\n", 239 acpi_device_name(device), acpi_device_bid(device)); 240 241 end: 242 if (result) { 243 kfree(button); 244 } 245 246 return_VALUE(result); 247 } 248 249 250 static int 251 acpi_button_remove (struct acpi_device *device, int type) 252 { 253 acpi_status status = 0; 254 struct acpi_button *button = NULL; 255 256 ACPI_FUNCTION_TRACE("acpi_button_remove"); 257 258 if (!device || !acpi_driver_data(device)) 259 return_VALUE(-EINVAL); 260 261 button = acpi_driver_data(device); 262 263 /* Unregister for device notifications. */ 264 switch (button->type) { 265 case ACPI_BUTTON_TYPE_POWERF: 266 status = acpi_remove_fixed_event_handler( 267 ACPI_EVENT_POWER_BUTTON, acpi_button_notify_fixed); 268 break; 269 case ACPI_BUTTON_TYPE_SLEEPF: 270 status = acpi_remove_fixed_event_handler( 271 ACPI_EVENT_SLEEP_BUTTON, acpi_button_notify_fixed); 272 break; 273 default: 274 status = acpi_remove_notify_handler(button->handle, 275 ACPI_DEVICE_NOTIFY, acpi_button_notify); 276 break; 277 } 278 279 if (ACPI_FAILURE(status)) 280 ACPI_DEBUG_PRINT((ACPI_DB_ERROR, 281 "Error removing notify handler\n")); 282 283 kfree(button); 284 285 return_VALUE(0); 286 } 287 288 289 static int __init 290 acpi_button_init (void) 291 { 292 int result = 0; 293 294 ACPI_FUNCTION_TRACE("acpi_button_init"); 295 296 result = acpi_bus_register_driver(&acpi_button_driver); 297 if (result < 0) { 298 return_VALUE(-ENODEV); 299 } 300 301 return_VALUE(0); 302 } 303 304 static void __exit 305 acpi_button_exit (void) 306 { 307 ACPI_FUNCTION_TRACE("acpi_button_exit"); 308 309 acpi_bus_unregister_driver(&acpi_button_driver); 310 311 return_VOID; 312 } 313 314 module_init(acpi_button_init); 315 module_exit(acpi_button_exit); 316