1 /* 2 * Copyright 2008-2013, Jérôme Duval, korli@users.berlios.de. 3 * 4 * Distributed under the terms of the MIT License. 5 */ 6 7 8 #include <ACPI.h> 9 10 #include <fs/select_sync_pool.h> 11 12 #include <stdio.h> 13 #include <stdlib.h> 14 #include <string.h> 15 16 17 #define ACPI_LID_MODULE_NAME "drivers/power/acpi_lid/driver_v1" 18 19 #define ACPI_LID_DEVICE_MODULE_NAME "drivers/power/acpi_lid/device_v1" 20 21 #define ACPI_NOTIFY_STATUS_CHANGED 0x80 22 23 /* Base Namespace devices are published to */ 24 #define ACPI_LID_BASENAME "power/acpi_lid/%d" 25 26 // name of pnp generator of path ids 27 #define ACPI_LID_PATHID_GENERATOR "acpi_lid/path_id" 28 29 //#define TRACE_LID 30 #ifdef TRACE_LID 31 # define TRACE(x...) dprintf("acpi_lid: " x) 32 #else 33 # define TRACE(x...) 34 #endif 35 #define ERROR(x...) dprintf("acpi_lid: " x) 36 37 38 static device_manager_info *sDeviceManager; 39 40 41 typedef struct acpi_ns_device_info { 42 device_node *node; 43 acpi_device_module_info *acpi; 44 acpi_device acpi_cookie; 45 uint8 last_status; 46 bool updated; 47 select_sync_pool* select_pool; 48 } acpi_lid_device_info; 49 50 51 static void 52 acpi_lid_read_status(acpi_lid_device_info *device) 53 { 54 acpi_data buf; 55 buf.pointer = NULL; 56 buf.length = ACPI_ALLOCATE_BUFFER; 57 if (device->acpi->evaluate_method(device->acpi_cookie, "_LID", NULL, 58 &buf) != B_OK 59 || buf.pointer == NULL 60 || ((acpi_object_type*)buf.pointer)->object_type != ACPI_TYPE_INTEGER) { 61 ERROR("couldn't get status\n"); 62 } else { 63 acpi_object_type* object = (acpi_object_type*)buf.pointer; 64 device->last_status = object->integer.integer; 65 device->updated = true; 66 free(buf.pointer); 67 TRACE("status %d\n", device->last_status); 68 } 69 } 70 71 72 static void 73 acpi_lid_notify_handler(acpi_handle _device, uint32 value, void *context) 74 { 75 acpi_lid_device_info *device = (acpi_lid_device_info *)context; 76 if (value == ACPI_NOTIFY_STATUS_CHANGED) { 77 TRACE("status changed\n"); 78 acpi_lid_read_status(device); 79 if (device->select_pool != NULL) 80 notify_select_event_pool(device->select_pool, B_SELECT_READ); 81 } else { 82 ERROR("unknown notification\n"); 83 } 84 85 } 86 87 88 // #pragma mark - device module API 89 90 91 static status_t 92 acpi_lid_init_device(void *driverCookie, void **cookie) 93 { 94 *cookie = driverCookie; 95 return B_OK; 96 } 97 98 99 static void 100 acpi_lid_uninit_device(void *_cookie) 101 { 102 103 } 104 105 106 static status_t 107 acpi_lid_open(void *_cookie, const char *path, int flags, void** cookie) 108 { 109 acpi_lid_device_info *device = (acpi_lid_device_info *)_cookie; 110 *cookie = device; 111 return B_OK; 112 } 113 114 115 static status_t 116 acpi_lid_read(void* _cookie, off_t position, void *buf, size_t* num_bytes) 117 { 118 acpi_lid_device_info* device = (acpi_lid_device_info*)_cookie; 119 if (*num_bytes < 1) 120 return B_IO_ERROR; 121 122 *((uint8 *)(buf)) = device->last_status; 123 *num_bytes = 1; 124 device->updated = false; 125 return B_OK; 126 } 127 128 129 static status_t 130 acpi_lid_write(void* cookie, off_t position, const void* buffer, size_t* num_bytes) 131 { 132 return B_ERROR; 133 } 134 135 136 static status_t 137 acpi_lid_control(void* _cookie, uint32 op, void* arg, size_t len) 138 { 139 return B_ERROR; 140 } 141 142 143 static status_t 144 acpi_lid_select(void *_cookie, uint8 event, selectsync *sync) 145 { 146 acpi_lid_device_info* device = (acpi_lid_device_info*)_cookie; 147 148 if (event != B_SELECT_READ) 149 return B_BAD_VALUE; 150 151 // add the event to the pool 152 status_t error = add_select_sync_pool_entry(&device->select_pool, sync, 153 event); 154 if (error != B_OK) { 155 ERROR("add_select_sync_pool_entry() failed: %#lx\n", error); 156 return error; 157 } 158 159 if (device->updated) 160 notify_select_event(sync, event); 161 162 return B_OK; 163 } 164 165 166 static status_t 167 acpi_lid_deselect(void *_cookie, uint8 event, selectsync *sync) 168 { 169 acpi_lid_device_info* device = (acpi_lid_device_info*)_cookie; 170 171 if (event != B_SELECT_READ) 172 return B_BAD_VALUE; 173 174 return remove_select_sync_pool_entry(&device->select_pool, sync, event); 175 } 176 177 178 static status_t 179 acpi_lid_close (void* cookie) 180 { 181 return B_OK; 182 } 183 184 185 static status_t 186 acpi_lid_free (void* cookie) 187 { 188 return B_OK; 189 } 190 191 192 // #pragma mark - driver module API 193 194 195 static float 196 acpi_lid_support(device_node *parent) 197 { 198 const char *bus; 199 uint32 device_type; 200 const char *hid; 201 202 // make sure parent is really the ACPI bus manager 203 if (sDeviceManager->get_attr_string(parent, B_DEVICE_BUS, &bus, false)) 204 return -1; 205 206 if (strcmp(bus, "acpi")) 207 return 0.0; 208 209 // check whether it's really a device 210 if (sDeviceManager->get_attr_uint32(parent, ACPI_DEVICE_TYPE_ITEM, 211 &device_type, false) != B_OK 212 || device_type != ACPI_TYPE_DEVICE) { 213 return 0.0; 214 } 215 216 // check whether it's a lid device 217 if (sDeviceManager->get_attr_string(parent, ACPI_DEVICE_HID_ITEM, &hid, 218 false) != B_OK || strcmp(hid, "PNP0C0D")) { 219 return 0.0; 220 } 221 222 dprintf("acpi_lid_support lid device found\n"); 223 224 return 0.6; 225 } 226 227 228 static status_t 229 acpi_lid_register_device(device_node *node) 230 { 231 device_attr attrs[] = { 232 { B_DEVICE_PRETTY_NAME, B_STRING_TYPE, { .string = "ACPI Lid" }}, 233 { NULL } 234 }; 235 236 return sDeviceManager->register_node(node, ACPI_LID_MODULE_NAME, attrs, 237 NULL, NULL); 238 } 239 240 241 static status_t 242 acpi_lid_init_driver(device_node *node, void **_driverCookie) 243 { 244 acpi_lid_device_info *device; 245 device_node *parent; 246 status_t status; 247 248 device = (acpi_lid_device_info *)calloc(1, sizeof(*device)); 249 if (device == NULL) 250 return B_NO_MEMORY; 251 252 device->node = node; 253 254 parent = sDeviceManager->get_parent_node(node); 255 sDeviceManager->get_driver(parent, (driver_module_info **)&device->acpi, 256 (void **)&device->acpi_cookie); 257 sDeviceManager->put_node(parent); 258 259 status = device->acpi->install_notify_handler(device->acpi_cookie, ACPI_DEVICE_NOTIFY, 260 acpi_lid_notify_handler, device); 261 if (status != B_OK) { 262 ERROR("can't install notify handler\n"); 263 } 264 265 device->last_status = 0; 266 device->updated = false; 267 device->select_pool = NULL; 268 269 *_driverCookie = device; 270 return B_OK; 271 } 272 273 274 static void 275 acpi_lid_uninit_driver(void *driverCookie) 276 { 277 acpi_lid_device_info *device = (acpi_lid_device_info *)driverCookie; 278 279 device->acpi->remove_notify_handler(device->acpi_cookie, ACPI_DEVICE_NOTIFY, 280 acpi_lid_notify_handler); 281 282 free(device); 283 } 284 285 286 static status_t 287 acpi_lid_register_child_devices(void *_cookie) 288 { 289 acpi_lid_device_info *device = (acpi_lid_device_info *)_cookie; 290 int path_id; 291 char name[128]; 292 293 path_id = sDeviceManager->create_id(ACPI_LID_PATHID_GENERATOR); 294 if (path_id < 0) { 295 ERROR("register_child_devices: couldn't create a path_id\n"); 296 return B_ERROR; 297 } 298 299 snprintf(name, sizeof(name), ACPI_LID_BASENAME, path_id); 300 301 return sDeviceManager->publish_device(device->node, name, 302 ACPI_LID_DEVICE_MODULE_NAME); 303 } 304 305 306 module_dependency module_dependencies[] = { 307 { B_DEVICE_MANAGER_MODULE_NAME, (module_info **)&sDeviceManager }, 308 {} 309 }; 310 311 312 driver_module_info acpi_lid_driver_module = { 313 { 314 ACPI_LID_MODULE_NAME, 315 0, 316 NULL 317 }, 318 319 acpi_lid_support, 320 acpi_lid_register_device, 321 acpi_lid_init_driver, 322 acpi_lid_uninit_driver, 323 acpi_lid_register_child_devices, 324 NULL, // rescan 325 NULL, // removed 326 }; 327 328 329 struct device_module_info acpi_lid_device_module = { 330 { 331 ACPI_LID_DEVICE_MODULE_NAME, 332 0, 333 NULL 334 }, 335 336 acpi_lid_init_device, 337 acpi_lid_uninit_device, 338 NULL, 339 340 acpi_lid_open, 341 acpi_lid_close, 342 acpi_lid_free, 343 acpi_lid_read, 344 acpi_lid_write, 345 NULL, 346 acpi_lid_control, 347 acpi_lid_select, 348 acpi_lid_deselect 349 }; 350 351 module_info *modules[] = { 352 (module_info *)&acpi_lid_driver_module, 353 (module_info *)&acpi_lid_device_module, 354 NULL 355 }; 356