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