1 /* 2 * acpi_button.c - ACPI Button Driver ($Revision: 29 $) 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 <precomp.h> 27 28 #define NDEBUG 29 #include <debug.h> 30 31 #define _COMPONENT ACPI_BUTTON_COMPONENT 32 ACPI_MODULE_NAME ("acpi_button") 33 34 35 static int acpi_button_add (struct acpi_device *device); 36 static int acpi_button_remove (struct acpi_device *device, int type); 37 38 static struct acpi_driver acpi_button_driver = { 39 {0,0}, 40 ACPI_BUTTON_DRIVER_NAME, 41 ACPI_BUTTON_CLASS, 42 0, 43 0, 44 "ACPI_FPB,ACPI_FSB,PNP0C0D,PNP0C0C,PNP0C0E", 45 {acpi_button_add,acpi_button_remove} 46 }; 47 48 struct acpi_button { 49 ACPI_HANDLE handle; 50 struct acpi_device *device; /* Fixed button kludge */ 51 UINT8 type; 52 unsigned long pushed; 53 }; 54 55 struct acpi_device *power_button; 56 struct acpi_device *sleep_button; 57 struct acpi_device *lid_button; 58 59 /* -------------------------------------------------------------------------- 60 Driver Interface 61 -------------------------------------------------------------------------- */ 62 63 void 64 acpi_button_notify ( 65 ACPI_HANDLE handle, 66 UINT32 event, 67 void *data) 68 { 69 struct acpi_button *button = (struct acpi_button *) data; 70 71 ACPI_FUNCTION_TRACE("acpi_button_notify"); 72 73 if (!button || !button->device) 74 return_VOID; 75 76 switch (event) { 77 case ACPI_BUTTON_NOTIFY_STATUS: 78 acpi_bus_generate_event(button->device, event, ++button->pushed); 79 break; 80 default: 81 ACPI_DEBUG_PRINT((ACPI_DB_INFO, 82 "Unsupported event [0x%x]\n", event)); 83 break; 84 } 85 86 return_VOID; 87 } 88 89 90 ACPI_STATUS 91 acpi_button_notify_fixed ( 92 void *data) 93 { 94 struct acpi_button *button = (struct acpi_button *) data; 95 96 ACPI_FUNCTION_TRACE("acpi_button_notify_fixed"); 97 98 if (!button) 99 return_ACPI_STATUS(AE_BAD_PARAMETER); 100 101 acpi_button_notify(button->handle, ACPI_BUTTON_NOTIFY_STATUS, button); 102 103 return_ACPI_STATUS(AE_OK); 104 } 105 106 107 static int 108 acpi_button_add ( 109 struct acpi_device *device) 110 { 111 int result = 0; 112 ACPI_STATUS status = AE_OK; 113 struct acpi_button *button = NULL; 114 115 ACPI_FUNCTION_TRACE("acpi_button_add"); 116 117 if (!device) 118 return_VALUE(-1); 119 120 button = ExAllocatePoolWithTag(NonPagedPool,sizeof(struct acpi_button), 'IPCA'); 121 if (!button) 122 return_VALUE(-4); 123 memset(button, 0, sizeof(struct acpi_button)); 124 125 button->device = device; 126 button->handle = device->handle; 127 acpi_driver_data(device) = button; 128 129 /* 130 * Determine the button type (via hid), as fixed-feature buttons 131 * need to be handled a bit differently than generic-space. 132 */ 133 if (!strcmp(acpi_device_hid(device), ACPI_BUTTON_HID_POWER)) { 134 button->type = ACPI_BUTTON_TYPE_POWER; 135 sprintf(acpi_device_name(device), "%s", 136 ACPI_BUTTON_DEVICE_NAME_POWER); 137 sprintf(acpi_device_class(device), "%s/%s", 138 ACPI_BUTTON_CLASS, ACPI_BUTTON_SUBCLASS_POWER); 139 } 140 else if (!strcmp(acpi_device_hid(device), ACPI_BUTTON_HID_POWERF)) { 141 button->type = ACPI_BUTTON_TYPE_POWERF; 142 sprintf(acpi_device_name(device), "%s", 143 ACPI_BUTTON_DEVICE_NAME_POWERF); 144 sprintf(acpi_device_class(device), "%s/%s", 145 ACPI_BUTTON_CLASS, ACPI_BUTTON_SUBCLASS_POWER); 146 } 147 else if (!strcmp(acpi_device_hid(device), ACPI_BUTTON_HID_SLEEP)) { 148 button->type = ACPI_BUTTON_TYPE_SLEEP; 149 sprintf(acpi_device_name(device), "%s", 150 ACPI_BUTTON_DEVICE_NAME_SLEEP); 151 sprintf(acpi_device_class(device), "%s/%s", 152 ACPI_BUTTON_CLASS, ACPI_BUTTON_SUBCLASS_SLEEP); 153 } 154 else if (!strcmp(acpi_device_hid(device), ACPI_BUTTON_HID_SLEEPF)) { 155 button->type = ACPI_BUTTON_TYPE_SLEEPF; 156 sprintf(acpi_device_name(device), "%s", 157 ACPI_BUTTON_DEVICE_NAME_SLEEPF); 158 sprintf(acpi_device_class(device), "%s/%s", 159 ACPI_BUTTON_CLASS, ACPI_BUTTON_SUBCLASS_SLEEP); 160 } 161 else if (!strcmp(acpi_device_hid(device), ACPI_BUTTON_HID_LID)) { 162 button->type = ACPI_BUTTON_TYPE_LID; 163 sprintf(acpi_device_name(device), "%s", 164 ACPI_BUTTON_DEVICE_NAME_LID); 165 sprintf(acpi_device_class(device), "%s/%s", 166 ACPI_BUTTON_CLASS, ACPI_BUTTON_SUBCLASS_LID); 167 } 168 else { 169 ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Unsupported hid [%s]\n", 170 acpi_device_hid(device))); 171 result = -15; 172 goto end; 173 } 174 175 /* 176 * Ensure only one button of each type is used. 177 */ 178 switch (button->type) { 179 case ACPI_BUTTON_TYPE_POWER: 180 case ACPI_BUTTON_TYPE_POWERF: 181 if (!power_button) 182 power_button = device; 183 else { 184 ExFreePoolWithTag(button, 'IPCA'); 185 return_VALUE(-15); 186 } 187 break; 188 case ACPI_BUTTON_TYPE_SLEEP: 189 case ACPI_BUTTON_TYPE_SLEEPF: 190 if (!sleep_button) 191 sleep_button = device; 192 else { 193 ExFreePoolWithTag(button, 'IPCA'); 194 return_VALUE(-15); 195 } 196 break; 197 case ACPI_BUTTON_TYPE_LID: 198 if (!lid_button) 199 lid_button = device; 200 else { 201 ExFreePoolWithTag(button, 'IPCA'); 202 return_VALUE(-15); 203 } 204 break; 205 } 206 207 switch (button->type) { 208 case ACPI_BUTTON_TYPE_POWERF: 209 status = AcpiInstallFixedEventHandler ( 210 ACPI_EVENT_POWER_BUTTON, 211 acpi_button_notify_fixed, 212 button); 213 break; 214 case ACPI_BUTTON_TYPE_SLEEPF: 215 status = AcpiInstallFixedEventHandler ( 216 ACPI_EVENT_SLEEP_BUTTON, 217 acpi_button_notify_fixed, 218 button); 219 break; 220 case ACPI_BUTTON_TYPE_LID: 221 status = AcpiInstallFixedEventHandler ( 222 ACPI_BUTTON_TYPE_LID, 223 acpi_button_notify_fixed, 224 button); 225 break; 226 default: 227 status = AcpiInstallNotifyHandler ( 228 button->handle, 229 ACPI_DEVICE_NOTIFY, 230 acpi_button_notify, 231 button); 232 break; 233 } 234 235 if (ACPI_FAILURE(status)) { 236 ACPI_DEBUG_PRINT((ACPI_DB_ERROR, 237 "Error installing notify handler\n")); 238 result = -15; 239 goto end; 240 } 241 242 DPRINT("%s [%s]\n", 243 acpi_device_name(device), acpi_device_bid(device)); 244 245 end: 246 if (result) { 247 ExFreePoolWithTag(button, 'IPCA'); 248 } 249 250 return_VALUE(result); 251 } 252 253 254 static int 255 acpi_button_remove (struct acpi_device *device, int type) 256 { 257 ACPI_STATUS status = 0; 258 struct acpi_button *button = NULL; 259 260 ACPI_FUNCTION_TRACE("acpi_button_remove"); 261 262 if (!device || !acpi_driver_data(device)) 263 return_VALUE(-1); 264 265 button = acpi_driver_data(device); 266 267 /* Unregister for device notifications. */ 268 switch (button->type) { 269 case ACPI_BUTTON_TYPE_POWERF: 270 status = AcpiRemoveFixedEventHandler( 271 ACPI_EVENT_POWER_BUTTON, acpi_button_notify_fixed); 272 break; 273 case ACPI_BUTTON_TYPE_SLEEPF: 274 status = AcpiRemoveFixedEventHandler( 275 ACPI_EVENT_SLEEP_BUTTON, acpi_button_notify_fixed); 276 break; 277 case ACPI_BUTTON_TYPE_LID: 278 status = AcpiRemoveFixedEventHandler( 279 ACPI_BUTTON_TYPE_LID, acpi_button_notify_fixed); 280 break; 281 default: 282 status = AcpiRemoveNotifyHandler(button->handle, 283 ACPI_DEVICE_NOTIFY, acpi_button_notify); 284 break; 285 } 286 287 if (ACPI_FAILURE(status)) 288 ACPI_DEBUG_PRINT((ACPI_DB_ERROR, 289 "Error removing notify handler\n")); 290 291 ExFreePoolWithTag(button, 'IPCA'); 292 293 return_VALUE(0); 294 } 295 296 297 int 298 acpi_button_init (void) 299 { 300 int result = 0; 301 302 ACPI_FUNCTION_TRACE("acpi_button_init"); 303 304 result = acpi_bus_register_driver(&acpi_button_driver); 305 if (result < 0) { 306 return_VALUE(-15); 307 } 308 309 return_VALUE(0); 310 } 311 312 313 void 314 acpi_button_exit (void) 315 { 316 ACPI_FUNCTION_TRACE("acpi_button_exit"); 317 318 acpi_bus_unregister_driver(&acpi_button_driver); 319 320 return_VOID; 321 } 322 323 324