diff options
Diffstat (limited to 'drivers/acpi/osl.c')
-rw-r--r-- | drivers/acpi/osl.c | 277 |
1 files changed, 273 insertions, 4 deletions
diff --git a/drivers/acpi/osl.c b/drivers/acpi/osl.c index 32d684af0ec7..67da6fb72274 100644 --- a/drivers/acpi/osl.c +++ b/drivers/acpi/osl.c @@ -220,6 +220,7 @@ void acpi_os_printf(const char *fmt, ...) acpi_os_vprintf(fmt, args); va_end(args); } +EXPORT_SYMBOL(acpi_os_printf); void acpi_os_vprintf(const char *fmt, va_list args) { @@ -234,7 +235,8 @@ void acpi_os_vprintf(const char *fmt, va_list args) printk(KERN_CONT "%s", buffer); } #else - printk(KERN_CONT "%s", buffer); + if (acpi_debugger_write_log(buffer) < 0) + printk(KERN_CONT "%s", buffer); #endif } @@ -364,6 +366,19 @@ static void acpi_unmap(acpi_physical_address pg_off, void __iomem *vaddr) iounmap(vaddr); } +/** + * acpi_os_map_iomem - Get a virtual address for a given physical address range. + * @phys: Start of the physical address range to map. + * @size: Size of the physical address range to map. + * + * Look up the given physical address range in the list of existing ACPI memory + * mappings. If found, get a reference to it and return a pointer to it (its + * virtual address). If not found, map it, add it to that list and return a + * pointer to it. + * + * During early init (when acpi_gbl_permanent_mmap has not been set yet) this + * routine simply calls __acpi_map_table() to get the job done. + */ void __iomem *__init_refok acpi_os_map_iomem(acpi_physical_address phys, acpi_size size) { @@ -439,6 +454,20 @@ static void acpi_os_map_cleanup(struct acpi_ioremap *map) } } +/** + * acpi_os_unmap_iomem - Drop a memory mapping reference. + * @virt: Start of the address range to drop a reference to. + * @size: Size of the address range to drop a reference to. + * + * Look up the given virtual address range in the list of existing ACPI memory + * mappings, drop a reference to it and unmap it if there are no more active + * references to it. + * + * During early init (when acpi_gbl_permanent_mmap has not been set yet) this + * routine simply calls __acpi_unmap_table() to get the job done. Since + * __acpi_unmap_table() is an __init function, the __ref annotation is needed + * here. + */ void __ref acpi_os_unmap_iomem(void __iomem *virt, acpi_size size) { struct acpi_ioremap *map; @@ -1101,6 +1130,200 @@ static void acpi_os_execute_deferred(struct work_struct *work) kfree(dpc); } +#ifdef CONFIG_ACPI_DEBUGGER +static struct acpi_debugger acpi_debugger; +static bool acpi_debugger_initialized; + +int acpi_register_debugger(struct module *owner, + const struct acpi_debugger_ops *ops) +{ + int ret = 0; + + mutex_lock(&acpi_debugger.lock); + if (acpi_debugger.ops) { + ret = -EBUSY; + goto err_lock; + } + + acpi_debugger.owner = owner; + acpi_debugger.ops = ops; + +err_lock: + mutex_unlock(&acpi_debugger.lock); + return ret; +} +EXPORT_SYMBOL(acpi_register_debugger); + +void acpi_unregister_debugger(const struct acpi_debugger_ops *ops) +{ + mutex_lock(&acpi_debugger.lock); + if (ops == acpi_debugger.ops) { + acpi_debugger.ops = NULL; + acpi_debugger.owner = NULL; + } + mutex_unlock(&acpi_debugger.lock); +} +EXPORT_SYMBOL(acpi_unregister_debugger); + +int acpi_debugger_create_thread(acpi_osd_exec_callback function, void *context) +{ + int ret; + int (*func)(acpi_osd_exec_callback, void *); + struct module *owner; + + if (!acpi_debugger_initialized) + return -ENODEV; + mutex_lock(&acpi_debugger.lock); + if (!acpi_debugger.ops) { + ret = -ENODEV; + goto err_lock; + } + if (!try_module_get(acpi_debugger.owner)) { + ret = -ENODEV; + goto err_lock; + } + func = acpi_debugger.ops->create_thread; + owner = acpi_debugger.owner; + mutex_unlock(&acpi_debugger.lock); + + ret = func(function, context); + + mutex_lock(&acpi_debugger.lock); + module_put(owner); +err_lock: + mutex_unlock(&acpi_debugger.lock); + return ret; +} + +ssize_t acpi_debugger_write_log(const char *msg) +{ + ssize_t ret; + ssize_t (*func)(const char *); + struct module *owner; + + if (!acpi_debugger_initialized) + return -ENODEV; + mutex_lock(&acpi_debugger.lock); + if (!acpi_debugger.ops) { + ret = -ENODEV; + goto err_lock; + } + if (!try_module_get(acpi_debugger.owner)) { + ret = -ENODEV; + goto err_lock; + } + func = acpi_debugger.ops->write_log; + owner = acpi_debugger.owner; + mutex_unlock(&acpi_debugger.lock); + + ret = func(msg); + + mutex_lock(&acpi_debugger.lock); + module_put(owner); +err_lock: + mutex_unlock(&acpi_debugger.lock); + return ret; +} + +ssize_t acpi_debugger_read_cmd(char *buffer, size_t buffer_length) +{ + ssize_t ret; + ssize_t (*func)(char *, size_t); + struct module *owner; + + if (!acpi_debugger_initialized) + return -ENODEV; + mutex_lock(&acpi_debugger.lock); + if (!acpi_debugger.ops) { + ret = -ENODEV; + goto err_lock; + } + if (!try_module_get(acpi_debugger.owner)) { + ret = -ENODEV; + goto err_lock; + } + func = acpi_debugger.ops->read_cmd; + owner = acpi_debugger.owner; + mutex_unlock(&acpi_debugger.lock); + + ret = func(buffer, buffer_length); + + mutex_lock(&acpi_debugger.lock); + module_put(owner); +err_lock: + mutex_unlock(&acpi_debugger.lock); + return ret; +} + +int acpi_debugger_wait_command_ready(void) +{ + int ret; + int (*func)(bool, char *, size_t); + struct module *owner; + + if (!acpi_debugger_initialized) + return -ENODEV; + mutex_lock(&acpi_debugger.lock); + if (!acpi_debugger.ops) { + ret = -ENODEV; + goto err_lock; + } + if (!try_module_get(acpi_debugger.owner)) { + ret = -ENODEV; + goto err_lock; + } + func = acpi_debugger.ops->wait_command_ready; + owner = acpi_debugger.owner; + mutex_unlock(&acpi_debugger.lock); + + ret = func(acpi_gbl_method_executing, + acpi_gbl_db_line_buf, ACPI_DB_LINE_BUFFER_SIZE); + + mutex_lock(&acpi_debugger.lock); + module_put(owner); +err_lock: + mutex_unlock(&acpi_debugger.lock); + return ret; +} + +int acpi_debugger_notify_command_complete(void) +{ + int ret; + int (*func)(void); + struct module *owner; + + if (!acpi_debugger_initialized) + return -ENODEV; + mutex_lock(&acpi_debugger.lock); + if (!acpi_debugger.ops) { + ret = -ENODEV; + goto err_lock; + } + if (!try_module_get(acpi_debugger.owner)) { + ret = -ENODEV; + goto err_lock; + } + func = acpi_debugger.ops->notify_command_complete; + owner = acpi_debugger.owner; + mutex_unlock(&acpi_debugger.lock); + + ret = func(); + + mutex_lock(&acpi_debugger.lock); + module_put(owner); +err_lock: + mutex_unlock(&acpi_debugger.lock); + return ret; +} + +int __init acpi_debugger_init(void) +{ + mutex_init(&acpi_debugger.lock); + acpi_debugger_initialized = true; + return 0; +} +#endif + /******************************************************************************* * * FUNCTION: acpi_os_execute @@ -1127,6 +1350,15 @@ acpi_status acpi_os_execute(acpi_execute_type type, "Scheduling function [%p(%p)] for deferred execution.\n", function, context)); + if (type == OSL_DEBUGGER_MAIN_THREAD) { + ret = acpi_debugger_create_thread(function, context); + if (ret) { + pr_err("Call to kthread_create() failed.\n"); + status = AE_ERROR; + } + goto out_thread; + } + /* * Allocate/initialize DPC structure. Note that this memory will be * freed by the callee. The kernel handles the work_struct list in a @@ -1151,11 +1383,17 @@ acpi_status acpi_os_execute(acpi_execute_type type, if (type == OSL_NOTIFY_HANDLER) { queue = kacpi_notify_wq; INIT_WORK(&dpc->work, acpi_os_execute_deferred); - } else { + } else if (type == OSL_GPE_HANDLER) { queue = kacpid_wq; INIT_WORK(&dpc->work, acpi_os_execute_deferred); + } else { + pr_err("Unsupported os_execute type %d.\n", type); + status = AE_ERROR; } + if (ACPI_FAILURE(status)) + goto err_workqueue; + /* * On some machines, a software-initiated SMI causes corruption unless * the SMI runs on CPU 0. An SMI can be initiated by any AML, but @@ -1164,13 +1402,15 @@ acpi_status acpi_os_execute(acpi_execute_type type, * queueing on CPU 0. */ ret = queue_work_on(0, queue, &dpc->work); - if (!ret) { printk(KERN_ERR PREFIX "Call to queue_work() failed.\n"); status = AE_ERROR; - kfree(dpc); } +err_workqueue: + if (ACPI_FAILURE(status)) + kfree(dpc); +out_thread: return status; } EXPORT_SYMBOL(acpi_os_execute); @@ -1358,10 +1598,39 @@ acpi_status acpi_os_get_line(char *buffer, u32 buffer_length, u32 *bytes_read) chars = strlen(buffer) - 1; buffer[chars] = '\0'; } +#else + int ret; + + ret = acpi_debugger_read_cmd(buffer, buffer_length); + if (ret < 0) + return AE_ERROR; + if (bytes_read) + *bytes_read = ret; #endif return AE_OK; } +EXPORT_SYMBOL(acpi_os_get_line); + +acpi_status acpi_os_wait_command_ready(void) +{ + int ret; + + ret = acpi_debugger_wait_command_ready(); + if (ret < 0) + return AE_ERROR; + return AE_OK; +} + +acpi_status acpi_os_notify_command_complete(void) +{ + int ret; + + ret = acpi_debugger_notify_command_complete(); + if (ret < 0) + return AE_ERROR; + return AE_OK; +} acpi_status acpi_os_signal(u32 function, void *info) { |