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