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