18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * ACPI AML interfacing support 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2015, Intel Corporation 68c2ecf20Sopenharmony_ci * Authors: Lv Zheng <lv.zheng@intel.com> 78c2ecf20Sopenharmony_ci */ 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci/* #define DEBUG */ 108c2ecf20Sopenharmony_ci#define pr_fmt(fmt) "ACPI: AML: " fmt 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci#include <linux/kernel.h> 138c2ecf20Sopenharmony_ci#include <linux/module.h> 148c2ecf20Sopenharmony_ci#include <linux/wait.h> 158c2ecf20Sopenharmony_ci#include <linux/poll.h> 168c2ecf20Sopenharmony_ci#include <linux/sched.h> 178c2ecf20Sopenharmony_ci#include <linux/kthread.h> 188c2ecf20Sopenharmony_ci#include <linux/proc_fs.h> 198c2ecf20Sopenharmony_ci#include <linux/debugfs.h> 208c2ecf20Sopenharmony_ci#include <linux/circ_buf.h> 218c2ecf20Sopenharmony_ci#include <linux/acpi.h> 228c2ecf20Sopenharmony_ci#include "internal.h" 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_ci#define ACPI_AML_BUF_ALIGN (sizeof (acpi_size)) 258c2ecf20Sopenharmony_ci#define ACPI_AML_BUF_SIZE PAGE_SIZE 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci#define circ_count(circ) \ 288c2ecf20Sopenharmony_ci (CIRC_CNT((circ)->head, (circ)->tail, ACPI_AML_BUF_SIZE)) 298c2ecf20Sopenharmony_ci#define circ_count_to_end(circ) \ 308c2ecf20Sopenharmony_ci (CIRC_CNT_TO_END((circ)->head, (circ)->tail, ACPI_AML_BUF_SIZE)) 318c2ecf20Sopenharmony_ci#define circ_space(circ) \ 328c2ecf20Sopenharmony_ci (CIRC_SPACE((circ)->head, (circ)->tail, ACPI_AML_BUF_SIZE)) 338c2ecf20Sopenharmony_ci#define circ_space_to_end(circ) \ 348c2ecf20Sopenharmony_ci (CIRC_SPACE_TO_END((circ)->head, (circ)->tail, ACPI_AML_BUF_SIZE)) 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci#define ACPI_AML_OPENED 0x0001 378c2ecf20Sopenharmony_ci#define ACPI_AML_CLOSED 0x0002 388c2ecf20Sopenharmony_ci#define ACPI_AML_IN_USER 0x0004 /* user space is writing cmd */ 398c2ecf20Sopenharmony_ci#define ACPI_AML_IN_KERN 0x0008 /* kernel space is reading cmd */ 408c2ecf20Sopenharmony_ci#define ACPI_AML_OUT_USER 0x0010 /* user space is reading log */ 418c2ecf20Sopenharmony_ci#define ACPI_AML_OUT_KERN 0x0020 /* kernel space is writing log */ 428c2ecf20Sopenharmony_ci#define ACPI_AML_USER (ACPI_AML_IN_USER | ACPI_AML_OUT_USER) 438c2ecf20Sopenharmony_ci#define ACPI_AML_KERN (ACPI_AML_IN_KERN | ACPI_AML_OUT_KERN) 448c2ecf20Sopenharmony_ci#define ACPI_AML_BUSY (ACPI_AML_USER | ACPI_AML_KERN) 458c2ecf20Sopenharmony_ci#define ACPI_AML_OPEN (ACPI_AML_OPENED | ACPI_AML_CLOSED) 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_cistruct acpi_aml_io { 488c2ecf20Sopenharmony_ci wait_queue_head_t wait; 498c2ecf20Sopenharmony_ci unsigned long flags; 508c2ecf20Sopenharmony_ci unsigned long users; 518c2ecf20Sopenharmony_ci struct mutex lock; 528c2ecf20Sopenharmony_ci struct task_struct *thread; 538c2ecf20Sopenharmony_ci char out_buf[ACPI_AML_BUF_SIZE] __aligned(ACPI_AML_BUF_ALIGN); 548c2ecf20Sopenharmony_ci struct circ_buf out_crc; 558c2ecf20Sopenharmony_ci char in_buf[ACPI_AML_BUF_SIZE] __aligned(ACPI_AML_BUF_ALIGN); 568c2ecf20Sopenharmony_ci struct circ_buf in_crc; 578c2ecf20Sopenharmony_ci acpi_osd_exec_callback function; 588c2ecf20Sopenharmony_ci void *context; 598c2ecf20Sopenharmony_ci unsigned long usages; 608c2ecf20Sopenharmony_ci}; 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_cistatic struct acpi_aml_io acpi_aml_io; 638c2ecf20Sopenharmony_cistatic bool acpi_aml_initialized; 648c2ecf20Sopenharmony_cistatic struct file *acpi_aml_active_reader; 658c2ecf20Sopenharmony_cistatic struct dentry *acpi_aml_dentry; 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_cistatic inline bool __acpi_aml_running(void) 688c2ecf20Sopenharmony_ci{ 698c2ecf20Sopenharmony_ci return acpi_aml_io.thread ? true : false; 708c2ecf20Sopenharmony_ci} 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_cistatic inline bool __acpi_aml_access_ok(unsigned long flag) 738c2ecf20Sopenharmony_ci{ 748c2ecf20Sopenharmony_ci /* 758c2ecf20Sopenharmony_ci * The debugger interface is in opened state (OPENED && !CLOSED), 768c2ecf20Sopenharmony_ci * then it is allowed to access the debugger buffers from either 778c2ecf20Sopenharmony_ci * user space or the kernel space. 788c2ecf20Sopenharmony_ci * In addition, for the kernel space, only the debugger thread 798c2ecf20Sopenharmony_ci * (thread ID matched) is allowed to access. 808c2ecf20Sopenharmony_ci */ 818c2ecf20Sopenharmony_ci if (!(acpi_aml_io.flags & ACPI_AML_OPENED) || 828c2ecf20Sopenharmony_ci (acpi_aml_io.flags & ACPI_AML_CLOSED) || 838c2ecf20Sopenharmony_ci !__acpi_aml_running()) 848c2ecf20Sopenharmony_ci return false; 858c2ecf20Sopenharmony_ci if ((flag & ACPI_AML_KERN) && 868c2ecf20Sopenharmony_ci current != acpi_aml_io.thread) 878c2ecf20Sopenharmony_ci return false; 888c2ecf20Sopenharmony_ci return true; 898c2ecf20Sopenharmony_ci} 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_cistatic inline bool __acpi_aml_readable(struct circ_buf *circ, unsigned long flag) 928c2ecf20Sopenharmony_ci{ 938c2ecf20Sopenharmony_ci /* 948c2ecf20Sopenharmony_ci * Another read is not in progress and there is data in buffer 958c2ecf20Sopenharmony_ci * available for read. 968c2ecf20Sopenharmony_ci */ 978c2ecf20Sopenharmony_ci if (!(acpi_aml_io.flags & flag) && circ_count(circ)) 988c2ecf20Sopenharmony_ci return true; 998c2ecf20Sopenharmony_ci return false; 1008c2ecf20Sopenharmony_ci} 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_cistatic inline bool __acpi_aml_writable(struct circ_buf *circ, unsigned long flag) 1038c2ecf20Sopenharmony_ci{ 1048c2ecf20Sopenharmony_ci /* 1058c2ecf20Sopenharmony_ci * Another write is not in progress and there is buffer space 1068c2ecf20Sopenharmony_ci * available for write. 1078c2ecf20Sopenharmony_ci */ 1088c2ecf20Sopenharmony_ci if (!(acpi_aml_io.flags & flag) && circ_space(circ)) 1098c2ecf20Sopenharmony_ci return true; 1108c2ecf20Sopenharmony_ci return false; 1118c2ecf20Sopenharmony_ci} 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_cistatic inline bool __acpi_aml_busy(void) 1148c2ecf20Sopenharmony_ci{ 1158c2ecf20Sopenharmony_ci if (acpi_aml_io.flags & ACPI_AML_BUSY) 1168c2ecf20Sopenharmony_ci return true; 1178c2ecf20Sopenharmony_ci return false; 1188c2ecf20Sopenharmony_ci} 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_cistatic inline bool __acpi_aml_opened(void) 1218c2ecf20Sopenharmony_ci{ 1228c2ecf20Sopenharmony_ci if (acpi_aml_io.flags & ACPI_AML_OPEN) 1238c2ecf20Sopenharmony_ci return true; 1248c2ecf20Sopenharmony_ci return false; 1258c2ecf20Sopenharmony_ci} 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_cistatic inline bool __acpi_aml_used(void) 1288c2ecf20Sopenharmony_ci{ 1298c2ecf20Sopenharmony_ci return acpi_aml_io.usages ? true : false; 1308c2ecf20Sopenharmony_ci} 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_cistatic inline bool acpi_aml_running(void) 1338c2ecf20Sopenharmony_ci{ 1348c2ecf20Sopenharmony_ci bool ret; 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci mutex_lock(&acpi_aml_io.lock); 1378c2ecf20Sopenharmony_ci ret = __acpi_aml_running(); 1388c2ecf20Sopenharmony_ci mutex_unlock(&acpi_aml_io.lock); 1398c2ecf20Sopenharmony_ci return ret; 1408c2ecf20Sopenharmony_ci} 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_cistatic bool acpi_aml_busy(void) 1438c2ecf20Sopenharmony_ci{ 1448c2ecf20Sopenharmony_ci bool ret; 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci mutex_lock(&acpi_aml_io.lock); 1478c2ecf20Sopenharmony_ci ret = __acpi_aml_busy(); 1488c2ecf20Sopenharmony_ci mutex_unlock(&acpi_aml_io.lock); 1498c2ecf20Sopenharmony_ci return ret; 1508c2ecf20Sopenharmony_ci} 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_cistatic bool acpi_aml_used(void) 1538c2ecf20Sopenharmony_ci{ 1548c2ecf20Sopenharmony_ci bool ret; 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci /* 1578c2ecf20Sopenharmony_ci * The usage count is prepared to avoid race conditions between the 1588c2ecf20Sopenharmony_ci * starts and the stops of the debugger thread. 1598c2ecf20Sopenharmony_ci */ 1608c2ecf20Sopenharmony_ci mutex_lock(&acpi_aml_io.lock); 1618c2ecf20Sopenharmony_ci ret = __acpi_aml_used(); 1628c2ecf20Sopenharmony_ci mutex_unlock(&acpi_aml_io.lock); 1638c2ecf20Sopenharmony_ci return ret; 1648c2ecf20Sopenharmony_ci} 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_cistatic bool acpi_aml_kern_readable(void) 1678c2ecf20Sopenharmony_ci{ 1688c2ecf20Sopenharmony_ci bool ret; 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_ci mutex_lock(&acpi_aml_io.lock); 1718c2ecf20Sopenharmony_ci ret = !__acpi_aml_access_ok(ACPI_AML_IN_KERN) || 1728c2ecf20Sopenharmony_ci __acpi_aml_readable(&acpi_aml_io.in_crc, ACPI_AML_IN_KERN); 1738c2ecf20Sopenharmony_ci mutex_unlock(&acpi_aml_io.lock); 1748c2ecf20Sopenharmony_ci return ret; 1758c2ecf20Sopenharmony_ci} 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_cistatic bool acpi_aml_kern_writable(void) 1788c2ecf20Sopenharmony_ci{ 1798c2ecf20Sopenharmony_ci bool ret; 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci mutex_lock(&acpi_aml_io.lock); 1828c2ecf20Sopenharmony_ci ret = !__acpi_aml_access_ok(ACPI_AML_OUT_KERN) || 1838c2ecf20Sopenharmony_ci __acpi_aml_writable(&acpi_aml_io.out_crc, ACPI_AML_OUT_KERN); 1848c2ecf20Sopenharmony_ci mutex_unlock(&acpi_aml_io.lock); 1858c2ecf20Sopenharmony_ci return ret; 1868c2ecf20Sopenharmony_ci} 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_cistatic bool acpi_aml_user_readable(void) 1898c2ecf20Sopenharmony_ci{ 1908c2ecf20Sopenharmony_ci bool ret; 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci mutex_lock(&acpi_aml_io.lock); 1938c2ecf20Sopenharmony_ci ret = !__acpi_aml_access_ok(ACPI_AML_OUT_USER) || 1948c2ecf20Sopenharmony_ci __acpi_aml_readable(&acpi_aml_io.out_crc, ACPI_AML_OUT_USER); 1958c2ecf20Sopenharmony_ci mutex_unlock(&acpi_aml_io.lock); 1968c2ecf20Sopenharmony_ci return ret; 1978c2ecf20Sopenharmony_ci} 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_cistatic bool acpi_aml_user_writable(void) 2008c2ecf20Sopenharmony_ci{ 2018c2ecf20Sopenharmony_ci bool ret; 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci mutex_lock(&acpi_aml_io.lock); 2048c2ecf20Sopenharmony_ci ret = !__acpi_aml_access_ok(ACPI_AML_IN_USER) || 2058c2ecf20Sopenharmony_ci __acpi_aml_writable(&acpi_aml_io.in_crc, ACPI_AML_IN_USER); 2068c2ecf20Sopenharmony_ci mutex_unlock(&acpi_aml_io.lock); 2078c2ecf20Sopenharmony_ci return ret; 2088c2ecf20Sopenharmony_ci} 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_cistatic int acpi_aml_lock_write(struct circ_buf *circ, unsigned long flag) 2118c2ecf20Sopenharmony_ci{ 2128c2ecf20Sopenharmony_ci int ret = 0; 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci mutex_lock(&acpi_aml_io.lock); 2158c2ecf20Sopenharmony_ci if (!__acpi_aml_access_ok(flag)) { 2168c2ecf20Sopenharmony_ci ret = -EFAULT; 2178c2ecf20Sopenharmony_ci goto out; 2188c2ecf20Sopenharmony_ci } 2198c2ecf20Sopenharmony_ci if (!__acpi_aml_writable(circ, flag)) { 2208c2ecf20Sopenharmony_ci ret = -EAGAIN; 2218c2ecf20Sopenharmony_ci goto out; 2228c2ecf20Sopenharmony_ci } 2238c2ecf20Sopenharmony_ci acpi_aml_io.flags |= flag; 2248c2ecf20Sopenharmony_ciout: 2258c2ecf20Sopenharmony_ci mutex_unlock(&acpi_aml_io.lock); 2268c2ecf20Sopenharmony_ci return ret; 2278c2ecf20Sopenharmony_ci} 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_cistatic int acpi_aml_lock_read(struct circ_buf *circ, unsigned long flag) 2308c2ecf20Sopenharmony_ci{ 2318c2ecf20Sopenharmony_ci int ret = 0; 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_ci mutex_lock(&acpi_aml_io.lock); 2348c2ecf20Sopenharmony_ci if (!__acpi_aml_access_ok(flag)) { 2358c2ecf20Sopenharmony_ci ret = -EFAULT; 2368c2ecf20Sopenharmony_ci goto out; 2378c2ecf20Sopenharmony_ci } 2388c2ecf20Sopenharmony_ci if (!__acpi_aml_readable(circ, flag)) { 2398c2ecf20Sopenharmony_ci ret = -EAGAIN; 2408c2ecf20Sopenharmony_ci goto out; 2418c2ecf20Sopenharmony_ci } 2428c2ecf20Sopenharmony_ci acpi_aml_io.flags |= flag; 2438c2ecf20Sopenharmony_ciout: 2448c2ecf20Sopenharmony_ci mutex_unlock(&acpi_aml_io.lock); 2458c2ecf20Sopenharmony_ci return ret; 2468c2ecf20Sopenharmony_ci} 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_cistatic void acpi_aml_unlock_fifo(unsigned long flag, bool wakeup) 2498c2ecf20Sopenharmony_ci{ 2508c2ecf20Sopenharmony_ci mutex_lock(&acpi_aml_io.lock); 2518c2ecf20Sopenharmony_ci acpi_aml_io.flags &= ~flag; 2528c2ecf20Sopenharmony_ci if (wakeup) 2538c2ecf20Sopenharmony_ci wake_up_interruptible(&acpi_aml_io.wait); 2548c2ecf20Sopenharmony_ci mutex_unlock(&acpi_aml_io.lock); 2558c2ecf20Sopenharmony_ci} 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_cistatic int acpi_aml_write_kern(const char *buf, int len) 2588c2ecf20Sopenharmony_ci{ 2598c2ecf20Sopenharmony_ci int ret; 2608c2ecf20Sopenharmony_ci struct circ_buf *crc = &acpi_aml_io.out_crc; 2618c2ecf20Sopenharmony_ci int n; 2628c2ecf20Sopenharmony_ci char *p; 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_ci ret = acpi_aml_lock_write(crc, ACPI_AML_OUT_KERN); 2658c2ecf20Sopenharmony_ci if (ret < 0) 2668c2ecf20Sopenharmony_ci return ret; 2678c2ecf20Sopenharmony_ci /* sync tail before inserting logs */ 2688c2ecf20Sopenharmony_ci smp_mb(); 2698c2ecf20Sopenharmony_ci p = &crc->buf[crc->head]; 2708c2ecf20Sopenharmony_ci n = min(len, circ_space_to_end(crc)); 2718c2ecf20Sopenharmony_ci memcpy(p, buf, n); 2728c2ecf20Sopenharmony_ci /* sync head after inserting logs */ 2738c2ecf20Sopenharmony_ci smp_wmb(); 2748c2ecf20Sopenharmony_ci crc->head = (crc->head + n) & (ACPI_AML_BUF_SIZE - 1); 2758c2ecf20Sopenharmony_ci acpi_aml_unlock_fifo(ACPI_AML_OUT_KERN, true); 2768c2ecf20Sopenharmony_ci return n; 2778c2ecf20Sopenharmony_ci} 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_cistatic int acpi_aml_readb_kern(void) 2808c2ecf20Sopenharmony_ci{ 2818c2ecf20Sopenharmony_ci int ret; 2828c2ecf20Sopenharmony_ci struct circ_buf *crc = &acpi_aml_io.in_crc; 2838c2ecf20Sopenharmony_ci char *p; 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_ci ret = acpi_aml_lock_read(crc, ACPI_AML_IN_KERN); 2868c2ecf20Sopenharmony_ci if (ret < 0) 2878c2ecf20Sopenharmony_ci return ret; 2888c2ecf20Sopenharmony_ci /* sync head before removing cmds */ 2898c2ecf20Sopenharmony_ci smp_rmb(); 2908c2ecf20Sopenharmony_ci p = &crc->buf[crc->tail]; 2918c2ecf20Sopenharmony_ci ret = (int)*p; 2928c2ecf20Sopenharmony_ci /* sync tail before inserting cmds */ 2938c2ecf20Sopenharmony_ci smp_mb(); 2948c2ecf20Sopenharmony_ci crc->tail = (crc->tail + 1) & (ACPI_AML_BUF_SIZE - 1); 2958c2ecf20Sopenharmony_ci acpi_aml_unlock_fifo(ACPI_AML_IN_KERN, true); 2968c2ecf20Sopenharmony_ci return ret; 2978c2ecf20Sopenharmony_ci} 2988c2ecf20Sopenharmony_ci 2998c2ecf20Sopenharmony_ci/* 3008c2ecf20Sopenharmony_ci * acpi_aml_write_log() - Capture debugger output 3018c2ecf20Sopenharmony_ci * @msg: the debugger output 3028c2ecf20Sopenharmony_ci * 3038c2ecf20Sopenharmony_ci * This function should be used to implement acpi_os_printf() to filter out 3048c2ecf20Sopenharmony_ci * the debugger output and store the output into the debugger interface 3058c2ecf20Sopenharmony_ci * buffer. Return the size of stored logs or errno. 3068c2ecf20Sopenharmony_ci */ 3078c2ecf20Sopenharmony_cistatic ssize_t acpi_aml_write_log(const char *msg) 3088c2ecf20Sopenharmony_ci{ 3098c2ecf20Sopenharmony_ci int ret = 0; 3108c2ecf20Sopenharmony_ci int count = 0, size = 0; 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_ci if (!acpi_aml_initialized) 3138c2ecf20Sopenharmony_ci return -ENODEV; 3148c2ecf20Sopenharmony_ci if (msg) 3158c2ecf20Sopenharmony_ci count = strlen(msg); 3168c2ecf20Sopenharmony_ci while (count > 0) { 3178c2ecf20Sopenharmony_ciagain: 3188c2ecf20Sopenharmony_ci ret = acpi_aml_write_kern(msg + size, count); 3198c2ecf20Sopenharmony_ci if (ret == -EAGAIN) { 3208c2ecf20Sopenharmony_ci ret = wait_event_interruptible(acpi_aml_io.wait, 3218c2ecf20Sopenharmony_ci acpi_aml_kern_writable()); 3228c2ecf20Sopenharmony_ci /* 3238c2ecf20Sopenharmony_ci * We need to retry when the condition 3248c2ecf20Sopenharmony_ci * becomes true. 3258c2ecf20Sopenharmony_ci */ 3268c2ecf20Sopenharmony_ci if (ret == 0) 3278c2ecf20Sopenharmony_ci goto again; 3288c2ecf20Sopenharmony_ci break; 3298c2ecf20Sopenharmony_ci } 3308c2ecf20Sopenharmony_ci if (ret < 0) 3318c2ecf20Sopenharmony_ci break; 3328c2ecf20Sopenharmony_ci size += ret; 3338c2ecf20Sopenharmony_ci count -= ret; 3348c2ecf20Sopenharmony_ci } 3358c2ecf20Sopenharmony_ci return size > 0 ? size : ret; 3368c2ecf20Sopenharmony_ci} 3378c2ecf20Sopenharmony_ci 3388c2ecf20Sopenharmony_ci/* 3398c2ecf20Sopenharmony_ci * acpi_aml_read_cmd() - Capture debugger input 3408c2ecf20Sopenharmony_ci * @msg: the debugger input 3418c2ecf20Sopenharmony_ci * @size: the size of the debugger input 3428c2ecf20Sopenharmony_ci * 3438c2ecf20Sopenharmony_ci * This function should be used to implement acpi_os_get_line() to capture 3448c2ecf20Sopenharmony_ci * the debugger input commands and store the input commands into the 3458c2ecf20Sopenharmony_ci * debugger interface buffer. Return the size of stored commands or errno. 3468c2ecf20Sopenharmony_ci */ 3478c2ecf20Sopenharmony_cistatic ssize_t acpi_aml_read_cmd(char *msg, size_t count) 3488c2ecf20Sopenharmony_ci{ 3498c2ecf20Sopenharmony_ci int ret = 0; 3508c2ecf20Sopenharmony_ci int size = 0; 3518c2ecf20Sopenharmony_ci 3528c2ecf20Sopenharmony_ci /* 3538c2ecf20Sopenharmony_ci * This is ensured by the running fact of the debugger thread 3548c2ecf20Sopenharmony_ci * unless a bug is introduced. 3558c2ecf20Sopenharmony_ci */ 3568c2ecf20Sopenharmony_ci BUG_ON(!acpi_aml_initialized); 3578c2ecf20Sopenharmony_ci while (count > 0) { 3588c2ecf20Sopenharmony_ciagain: 3598c2ecf20Sopenharmony_ci /* 3608c2ecf20Sopenharmony_ci * Check each input byte to find the end of the command. 3618c2ecf20Sopenharmony_ci */ 3628c2ecf20Sopenharmony_ci ret = acpi_aml_readb_kern(); 3638c2ecf20Sopenharmony_ci if (ret == -EAGAIN) { 3648c2ecf20Sopenharmony_ci ret = wait_event_interruptible(acpi_aml_io.wait, 3658c2ecf20Sopenharmony_ci acpi_aml_kern_readable()); 3668c2ecf20Sopenharmony_ci /* 3678c2ecf20Sopenharmony_ci * We need to retry when the condition becomes 3688c2ecf20Sopenharmony_ci * true. 3698c2ecf20Sopenharmony_ci */ 3708c2ecf20Sopenharmony_ci if (ret == 0) 3718c2ecf20Sopenharmony_ci goto again; 3728c2ecf20Sopenharmony_ci } 3738c2ecf20Sopenharmony_ci if (ret < 0) 3748c2ecf20Sopenharmony_ci break; 3758c2ecf20Sopenharmony_ci *(msg + size) = (char)ret; 3768c2ecf20Sopenharmony_ci size++; 3778c2ecf20Sopenharmony_ci count--; 3788c2ecf20Sopenharmony_ci if (ret == '\n') { 3798c2ecf20Sopenharmony_ci /* 3808c2ecf20Sopenharmony_ci * acpi_os_get_line() requires a zero terminated command 3818c2ecf20Sopenharmony_ci * string. 3828c2ecf20Sopenharmony_ci */ 3838c2ecf20Sopenharmony_ci *(msg + size - 1) = '\0'; 3848c2ecf20Sopenharmony_ci break; 3858c2ecf20Sopenharmony_ci } 3868c2ecf20Sopenharmony_ci } 3878c2ecf20Sopenharmony_ci return size > 0 ? size : ret; 3888c2ecf20Sopenharmony_ci} 3898c2ecf20Sopenharmony_ci 3908c2ecf20Sopenharmony_cistatic int acpi_aml_thread(void *unused) 3918c2ecf20Sopenharmony_ci{ 3928c2ecf20Sopenharmony_ci acpi_osd_exec_callback function = NULL; 3938c2ecf20Sopenharmony_ci void *context; 3948c2ecf20Sopenharmony_ci 3958c2ecf20Sopenharmony_ci mutex_lock(&acpi_aml_io.lock); 3968c2ecf20Sopenharmony_ci if (acpi_aml_io.function) { 3978c2ecf20Sopenharmony_ci acpi_aml_io.usages++; 3988c2ecf20Sopenharmony_ci function = acpi_aml_io.function; 3998c2ecf20Sopenharmony_ci context = acpi_aml_io.context; 4008c2ecf20Sopenharmony_ci } 4018c2ecf20Sopenharmony_ci mutex_unlock(&acpi_aml_io.lock); 4028c2ecf20Sopenharmony_ci 4038c2ecf20Sopenharmony_ci if (function) 4048c2ecf20Sopenharmony_ci function(context); 4058c2ecf20Sopenharmony_ci 4068c2ecf20Sopenharmony_ci mutex_lock(&acpi_aml_io.lock); 4078c2ecf20Sopenharmony_ci acpi_aml_io.usages--; 4088c2ecf20Sopenharmony_ci if (!__acpi_aml_used()) { 4098c2ecf20Sopenharmony_ci acpi_aml_io.thread = NULL; 4108c2ecf20Sopenharmony_ci wake_up(&acpi_aml_io.wait); 4118c2ecf20Sopenharmony_ci } 4128c2ecf20Sopenharmony_ci mutex_unlock(&acpi_aml_io.lock); 4138c2ecf20Sopenharmony_ci 4148c2ecf20Sopenharmony_ci return 0; 4158c2ecf20Sopenharmony_ci} 4168c2ecf20Sopenharmony_ci 4178c2ecf20Sopenharmony_ci/* 4188c2ecf20Sopenharmony_ci * acpi_aml_create_thread() - Create AML debugger thread 4198c2ecf20Sopenharmony_ci * @function: the debugger thread callback 4208c2ecf20Sopenharmony_ci * @context: the context to be passed to the debugger thread 4218c2ecf20Sopenharmony_ci * 4228c2ecf20Sopenharmony_ci * This function should be used to implement acpi_os_execute() which is 4238c2ecf20Sopenharmony_ci * used by the ACPICA debugger to create the debugger thread. 4248c2ecf20Sopenharmony_ci */ 4258c2ecf20Sopenharmony_cistatic int acpi_aml_create_thread(acpi_osd_exec_callback function, void *context) 4268c2ecf20Sopenharmony_ci{ 4278c2ecf20Sopenharmony_ci struct task_struct *t; 4288c2ecf20Sopenharmony_ci 4298c2ecf20Sopenharmony_ci mutex_lock(&acpi_aml_io.lock); 4308c2ecf20Sopenharmony_ci acpi_aml_io.function = function; 4318c2ecf20Sopenharmony_ci acpi_aml_io.context = context; 4328c2ecf20Sopenharmony_ci mutex_unlock(&acpi_aml_io.lock); 4338c2ecf20Sopenharmony_ci 4348c2ecf20Sopenharmony_ci t = kthread_create(acpi_aml_thread, NULL, "aml"); 4358c2ecf20Sopenharmony_ci if (IS_ERR(t)) { 4368c2ecf20Sopenharmony_ci pr_err("Failed to create AML debugger thread.\n"); 4378c2ecf20Sopenharmony_ci return PTR_ERR(t); 4388c2ecf20Sopenharmony_ci } 4398c2ecf20Sopenharmony_ci 4408c2ecf20Sopenharmony_ci mutex_lock(&acpi_aml_io.lock); 4418c2ecf20Sopenharmony_ci acpi_aml_io.thread = t; 4428c2ecf20Sopenharmony_ci acpi_set_debugger_thread_id((acpi_thread_id)(unsigned long)t); 4438c2ecf20Sopenharmony_ci wake_up_process(t); 4448c2ecf20Sopenharmony_ci mutex_unlock(&acpi_aml_io.lock); 4458c2ecf20Sopenharmony_ci return 0; 4468c2ecf20Sopenharmony_ci} 4478c2ecf20Sopenharmony_ci 4488c2ecf20Sopenharmony_cistatic int acpi_aml_wait_command_ready(bool single_step, 4498c2ecf20Sopenharmony_ci char *buffer, size_t length) 4508c2ecf20Sopenharmony_ci{ 4518c2ecf20Sopenharmony_ci acpi_status status; 4528c2ecf20Sopenharmony_ci 4538c2ecf20Sopenharmony_ci if (single_step) 4548c2ecf20Sopenharmony_ci acpi_os_printf("\n%1c ", ACPI_DEBUGGER_EXECUTE_PROMPT); 4558c2ecf20Sopenharmony_ci else 4568c2ecf20Sopenharmony_ci acpi_os_printf("\n%1c ", ACPI_DEBUGGER_COMMAND_PROMPT); 4578c2ecf20Sopenharmony_ci 4588c2ecf20Sopenharmony_ci status = acpi_os_get_line(buffer, length, NULL); 4598c2ecf20Sopenharmony_ci if (ACPI_FAILURE(status)) 4608c2ecf20Sopenharmony_ci return -EINVAL; 4618c2ecf20Sopenharmony_ci return 0; 4628c2ecf20Sopenharmony_ci} 4638c2ecf20Sopenharmony_ci 4648c2ecf20Sopenharmony_cistatic int acpi_aml_notify_command_complete(void) 4658c2ecf20Sopenharmony_ci{ 4668c2ecf20Sopenharmony_ci return 0; 4678c2ecf20Sopenharmony_ci} 4688c2ecf20Sopenharmony_ci 4698c2ecf20Sopenharmony_cistatic int acpi_aml_open(struct inode *inode, struct file *file) 4708c2ecf20Sopenharmony_ci{ 4718c2ecf20Sopenharmony_ci int ret = 0; 4728c2ecf20Sopenharmony_ci acpi_status status; 4738c2ecf20Sopenharmony_ci 4748c2ecf20Sopenharmony_ci mutex_lock(&acpi_aml_io.lock); 4758c2ecf20Sopenharmony_ci /* 4768c2ecf20Sopenharmony_ci * The debugger interface is being closed, no new user is allowed 4778c2ecf20Sopenharmony_ci * during this period. 4788c2ecf20Sopenharmony_ci */ 4798c2ecf20Sopenharmony_ci if (acpi_aml_io.flags & ACPI_AML_CLOSED) { 4808c2ecf20Sopenharmony_ci ret = -EBUSY; 4818c2ecf20Sopenharmony_ci goto err_lock; 4828c2ecf20Sopenharmony_ci } 4838c2ecf20Sopenharmony_ci if ((file->f_flags & O_ACCMODE) != O_WRONLY) { 4848c2ecf20Sopenharmony_ci /* 4858c2ecf20Sopenharmony_ci * Only one reader is allowed to initiate the debugger 4868c2ecf20Sopenharmony_ci * thread. 4878c2ecf20Sopenharmony_ci */ 4888c2ecf20Sopenharmony_ci if (acpi_aml_active_reader) { 4898c2ecf20Sopenharmony_ci ret = -EBUSY; 4908c2ecf20Sopenharmony_ci goto err_lock; 4918c2ecf20Sopenharmony_ci } else { 4928c2ecf20Sopenharmony_ci pr_debug("Opening debugger reader.\n"); 4938c2ecf20Sopenharmony_ci acpi_aml_active_reader = file; 4948c2ecf20Sopenharmony_ci } 4958c2ecf20Sopenharmony_ci } else { 4968c2ecf20Sopenharmony_ci /* 4978c2ecf20Sopenharmony_ci * No writer is allowed unless the debugger thread is 4988c2ecf20Sopenharmony_ci * ready. 4998c2ecf20Sopenharmony_ci */ 5008c2ecf20Sopenharmony_ci if (!(acpi_aml_io.flags & ACPI_AML_OPENED)) { 5018c2ecf20Sopenharmony_ci ret = -ENODEV; 5028c2ecf20Sopenharmony_ci goto err_lock; 5038c2ecf20Sopenharmony_ci } 5048c2ecf20Sopenharmony_ci } 5058c2ecf20Sopenharmony_ci if (acpi_aml_active_reader == file) { 5068c2ecf20Sopenharmony_ci pr_debug("Opening debugger interface.\n"); 5078c2ecf20Sopenharmony_ci mutex_unlock(&acpi_aml_io.lock); 5088c2ecf20Sopenharmony_ci 5098c2ecf20Sopenharmony_ci pr_debug("Initializing debugger thread.\n"); 5108c2ecf20Sopenharmony_ci status = acpi_initialize_debugger(); 5118c2ecf20Sopenharmony_ci if (ACPI_FAILURE(status)) { 5128c2ecf20Sopenharmony_ci pr_err("Failed to initialize debugger.\n"); 5138c2ecf20Sopenharmony_ci ret = -EINVAL; 5148c2ecf20Sopenharmony_ci goto err_exit; 5158c2ecf20Sopenharmony_ci } 5168c2ecf20Sopenharmony_ci pr_debug("Debugger thread initialized.\n"); 5178c2ecf20Sopenharmony_ci 5188c2ecf20Sopenharmony_ci mutex_lock(&acpi_aml_io.lock); 5198c2ecf20Sopenharmony_ci acpi_aml_io.flags |= ACPI_AML_OPENED; 5208c2ecf20Sopenharmony_ci acpi_aml_io.out_crc.head = acpi_aml_io.out_crc.tail = 0; 5218c2ecf20Sopenharmony_ci acpi_aml_io.in_crc.head = acpi_aml_io.in_crc.tail = 0; 5228c2ecf20Sopenharmony_ci pr_debug("Debugger interface opened.\n"); 5238c2ecf20Sopenharmony_ci } 5248c2ecf20Sopenharmony_ci acpi_aml_io.users++; 5258c2ecf20Sopenharmony_cierr_lock: 5268c2ecf20Sopenharmony_ci if (ret < 0) { 5278c2ecf20Sopenharmony_ci if (acpi_aml_active_reader == file) 5288c2ecf20Sopenharmony_ci acpi_aml_active_reader = NULL; 5298c2ecf20Sopenharmony_ci } 5308c2ecf20Sopenharmony_ci mutex_unlock(&acpi_aml_io.lock); 5318c2ecf20Sopenharmony_cierr_exit: 5328c2ecf20Sopenharmony_ci return ret; 5338c2ecf20Sopenharmony_ci} 5348c2ecf20Sopenharmony_ci 5358c2ecf20Sopenharmony_cistatic int acpi_aml_release(struct inode *inode, struct file *file) 5368c2ecf20Sopenharmony_ci{ 5378c2ecf20Sopenharmony_ci mutex_lock(&acpi_aml_io.lock); 5388c2ecf20Sopenharmony_ci acpi_aml_io.users--; 5398c2ecf20Sopenharmony_ci if (file == acpi_aml_active_reader) { 5408c2ecf20Sopenharmony_ci pr_debug("Closing debugger reader.\n"); 5418c2ecf20Sopenharmony_ci acpi_aml_active_reader = NULL; 5428c2ecf20Sopenharmony_ci 5438c2ecf20Sopenharmony_ci pr_debug("Closing debugger interface.\n"); 5448c2ecf20Sopenharmony_ci acpi_aml_io.flags |= ACPI_AML_CLOSED; 5458c2ecf20Sopenharmony_ci 5468c2ecf20Sopenharmony_ci /* 5478c2ecf20Sopenharmony_ci * Wake up all user space/kernel space blocked 5488c2ecf20Sopenharmony_ci * readers/writers. 5498c2ecf20Sopenharmony_ci */ 5508c2ecf20Sopenharmony_ci wake_up_interruptible(&acpi_aml_io.wait); 5518c2ecf20Sopenharmony_ci mutex_unlock(&acpi_aml_io.lock); 5528c2ecf20Sopenharmony_ci /* 5538c2ecf20Sopenharmony_ci * Wait all user space/kernel space readers/writers to 5548c2ecf20Sopenharmony_ci * stop so that ACPICA command loop of the debugger thread 5558c2ecf20Sopenharmony_ci * should fail all its command line reads after this point. 5568c2ecf20Sopenharmony_ci */ 5578c2ecf20Sopenharmony_ci wait_event(acpi_aml_io.wait, !acpi_aml_busy()); 5588c2ecf20Sopenharmony_ci 5598c2ecf20Sopenharmony_ci /* 5608c2ecf20Sopenharmony_ci * Then we try to terminate the debugger thread if it is 5618c2ecf20Sopenharmony_ci * not terminated. 5628c2ecf20Sopenharmony_ci */ 5638c2ecf20Sopenharmony_ci pr_debug("Terminating debugger thread.\n"); 5648c2ecf20Sopenharmony_ci acpi_terminate_debugger(); 5658c2ecf20Sopenharmony_ci wait_event(acpi_aml_io.wait, !acpi_aml_used()); 5668c2ecf20Sopenharmony_ci pr_debug("Debugger thread terminated.\n"); 5678c2ecf20Sopenharmony_ci 5688c2ecf20Sopenharmony_ci mutex_lock(&acpi_aml_io.lock); 5698c2ecf20Sopenharmony_ci acpi_aml_io.flags &= ~ACPI_AML_OPENED; 5708c2ecf20Sopenharmony_ci } 5718c2ecf20Sopenharmony_ci if (acpi_aml_io.users == 0) { 5728c2ecf20Sopenharmony_ci pr_debug("Debugger interface closed.\n"); 5738c2ecf20Sopenharmony_ci acpi_aml_io.flags &= ~ACPI_AML_CLOSED; 5748c2ecf20Sopenharmony_ci } 5758c2ecf20Sopenharmony_ci mutex_unlock(&acpi_aml_io.lock); 5768c2ecf20Sopenharmony_ci return 0; 5778c2ecf20Sopenharmony_ci} 5788c2ecf20Sopenharmony_ci 5798c2ecf20Sopenharmony_cistatic int acpi_aml_read_user(char __user *buf, int len) 5808c2ecf20Sopenharmony_ci{ 5818c2ecf20Sopenharmony_ci int ret; 5828c2ecf20Sopenharmony_ci struct circ_buf *crc = &acpi_aml_io.out_crc; 5838c2ecf20Sopenharmony_ci int n; 5848c2ecf20Sopenharmony_ci char *p; 5858c2ecf20Sopenharmony_ci 5868c2ecf20Sopenharmony_ci ret = acpi_aml_lock_read(crc, ACPI_AML_OUT_USER); 5878c2ecf20Sopenharmony_ci if (ret < 0) 5888c2ecf20Sopenharmony_ci return ret; 5898c2ecf20Sopenharmony_ci /* sync head before removing logs */ 5908c2ecf20Sopenharmony_ci smp_rmb(); 5918c2ecf20Sopenharmony_ci p = &crc->buf[crc->tail]; 5928c2ecf20Sopenharmony_ci n = min(len, circ_count_to_end(crc)); 5938c2ecf20Sopenharmony_ci if (copy_to_user(buf, p, n)) { 5948c2ecf20Sopenharmony_ci ret = -EFAULT; 5958c2ecf20Sopenharmony_ci goto out; 5968c2ecf20Sopenharmony_ci } 5978c2ecf20Sopenharmony_ci /* sync tail after removing logs */ 5988c2ecf20Sopenharmony_ci smp_mb(); 5998c2ecf20Sopenharmony_ci crc->tail = (crc->tail + n) & (ACPI_AML_BUF_SIZE - 1); 6008c2ecf20Sopenharmony_ci ret = n; 6018c2ecf20Sopenharmony_ciout: 6028c2ecf20Sopenharmony_ci acpi_aml_unlock_fifo(ACPI_AML_OUT_USER, ret >= 0); 6038c2ecf20Sopenharmony_ci return ret; 6048c2ecf20Sopenharmony_ci} 6058c2ecf20Sopenharmony_ci 6068c2ecf20Sopenharmony_cistatic ssize_t acpi_aml_read(struct file *file, char __user *buf, 6078c2ecf20Sopenharmony_ci size_t count, loff_t *ppos) 6088c2ecf20Sopenharmony_ci{ 6098c2ecf20Sopenharmony_ci int ret = 0; 6108c2ecf20Sopenharmony_ci int size = 0; 6118c2ecf20Sopenharmony_ci 6128c2ecf20Sopenharmony_ci if (!count) 6138c2ecf20Sopenharmony_ci return 0; 6148c2ecf20Sopenharmony_ci if (!access_ok(buf, count)) 6158c2ecf20Sopenharmony_ci return -EFAULT; 6168c2ecf20Sopenharmony_ci 6178c2ecf20Sopenharmony_ci while (count > 0) { 6188c2ecf20Sopenharmony_ciagain: 6198c2ecf20Sopenharmony_ci ret = acpi_aml_read_user(buf + size, count); 6208c2ecf20Sopenharmony_ci if (ret == -EAGAIN) { 6218c2ecf20Sopenharmony_ci if (file->f_flags & O_NONBLOCK) 6228c2ecf20Sopenharmony_ci break; 6238c2ecf20Sopenharmony_ci else { 6248c2ecf20Sopenharmony_ci ret = wait_event_interruptible(acpi_aml_io.wait, 6258c2ecf20Sopenharmony_ci acpi_aml_user_readable()); 6268c2ecf20Sopenharmony_ci /* 6278c2ecf20Sopenharmony_ci * We need to retry when the condition 6288c2ecf20Sopenharmony_ci * becomes true. 6298c2ecf20Sopenharmony_ci */ 6308c2ecf20Sopenharmony_ci if (ret == 0) 6318c2ecf20Sopenharmony_ci goto again; 6328c2ecf20Sopenharmony_ci } 6338c2ecf20Sopenharmony_ci } 6348c2ecf20Sopenharmony_ci if (ret < 0) { 6358c2ecf20Sopenharmony_ci if (!acpi_aml_running()) 6368c2ecf20Sopenharmony_ci ret = 0; 6378c2ecf20Sopenharmony_ci break; 6388c2ecf20Sopenharmony_ci } 6398c2ecf20Sopenharmony_ci if (ret) { 6408c2ecf20Sopenharmony_ci size += ret; 6418c2ecf20Sopenharmony_ci count -= ret; 6428c2ecf20Sopenharmony_ci *ppos += ret; 6438c2ecf20Sopenharmony_ci break; 6448c2ecf20Sopenharmony_ci } 6458c2ecf20Sopenharmony_ci } 6468c2ecf20Sopenharmony_ci return size > 0 ? size : ret; 6478c2ecf20Sopenharmony_ci} 6488c2ecf20Sopenharmony_ci 6498c2ecf20Sopenharmony_cistatic int acpi_aml_write_user(const char __user *buf, int len) 6508c2ecf20Sopenharmony_ci{ 6518c2ecf20Sopenharmony_ci int ret; 6528c2ecf20Sopenharmony_ci struct circ_buf *crc = &acpi_aml_io.in_crc; 6538c2ecf20Sopenharmony_ci int n; 6548c2ecf20Sopenharmony_ci char *p; 6558c2ecf20Sopenharmony_ci 6568c2ecf20Sopenharmony_ci ret = acpi_aml_lock_write(crc, ACPI_AML_IN_USER); 6578c2ecf20Sopenharmony_ci if (ret < 0) 6588c2ecf20Sopenharmony_ci return ret; 6598c2ecf20Sopenharmony_ci /* sync tail before inserting cmds */ 6608c2ecf20Sopenharmony_ci smp_mb(); 6618c2ecf20Sopenharmony_ci p = &crc->buf[crc->head]; 6628c2ecf20Sopenharmony_ci n = min(len, circ_space_to_end(crc)); 6638c2ecf20Sopenharmony_ci if (copy_from_user(p, buf, n)) { 6648c2ecf20Sopenharmony_ci ret = -EFAULT; 6658c2ecf20Sopenharmony_ci goto out; 6668c2ecf20Sopenharmony_ci } 6678c2ecf20Sopenharmony_ci /* sync head after inserting cmds */ 6688c2ecf20Sopenharmony_ci smp_wmb(); 6698c2ecf20Sopenharmony_ci crc->head = (crc->head + n) & (ACPI_AML_BUF_SIZE - 1); 6708c2ecf20Sopenharmony_ci ret = n; 6718c2ecf20Sopenharmony_ciout: 6728c2ecf20Sopenharmony_ci acpi_aml_unlock_fifo(ACPI_AML_IN_USER, ret >= 0); 6738c2ecf20Sopenharmony_ci return n; 6748c2ecf20Sopenharmony_ci} 6758c2ecf20Sopenharmony_ci 6768c2ecf20Sopenharmony_cistatic ssize_t acpi_aml_write(struct file *file, const char __user *buf, 6778c2ecf20Sopenharmony_ci size_t count, loff_t *ppos) 6788c2ecf20Sopenharmony_ci{ 6798c2ecf20Sopenharmony_ci int ret = 0; 6808c2ecf20Sopenharmony_ci int size = 0; 6818c2ecf20Sopenharmony_ci 6828c2ecf20Sopenharmony_ci if (!count) 6838c2ecf20Sopenharmony_ci return 0; 6848c2ecf20Sopenharmony_ci if (!access_ok(buf, count)) 6858c2ecf20Sopenharmony_ci return -EFAULT; 6868c2ecf20Sopenharmony_ci 6878c2ecf20Sopenharmony_ci while (count > 0) { 6888c2ecf20Sopenharmony_ciagain: 6898c2ecf20Sopenharmony_ci ret = acpi_aml_write_user(buf + size, count); 6908c2ecf20Sopenharmony_ci if (ret == -EAGAIN) { 6918c2ecf20Sopenharmony_ci if (file->f_flags & O_NONBLOCK) 6928c2ecf20Sopenharmony_ci break; 6938c2ecf20Sopenharmony_ci else { 6948c2ecf20Sopenharmony_ci ret = wait_event_interruptible(acpi_aml_io.wait, 6958c2ecf20Sopenharmony_ci acpi_aml_user_writable()); 6968c2ecf20Sopenharmony_ci /* 6978c2ecf20Sopenharmony_ci * We need to retry when the condition 6988c2ecf20Sopenharmony_ci * becomes true. 6998c2ecf20Sopenharmony_ci */ 7008c2ecf20Sopenharmony_ci if (ret == 0) 7018c2ecf20Sopenharmony_ci goto again; 7028c2ecf20Sopenharmony_ci } 7038c2ecf20Sopenharmony_ci } 7048c2ecf20Sopenharmony_ci if (ret < 0) { 7058c2ecf20Sopenharmony_ci if (!acpi_aml_running()) 7068c2ecf20Sopenharmony_ci ret = 0; 7078c2ecf20Sopenharmony_ci break; 7088c2ecf20Sopenharmony_ci } 7098c2ecf20Sopenharmony_ci if (ret) { 7108c2ecf20Sopenharmony_ci size += ret; 7118c2ecf20Sopenharmony_ci count -= ret; 7128c2ecf20Sopenharmony_ci *ppos += ret; 7138c2ecf20Sopenharmony_ci } 7148c2ecf20Sopenharmony_ci } 7158c2ecf20Sopenharmony_ci return size > 0 ? size : ret; 7168c2ecf20Sopenharmony_ci} 7178c2ecf20Sopenharmony_ci 7188c2ecf20Sopenharmony_cistatic __poll_t acpi_aml_poll(struct file *file, poll_table *wait) 7198c2ecf20Sopenharmony_ci{ 7208c2ecf20Sopenharmony_ci __poll_t masks = 0; 7218c2ecf20Sopenharmony_ci 7228c2ecf20Sopenharmony_ci poll_wait(file, &acpi_aml_io.wait, wait); 7238c2ecf20Sopenharmony_ci if (acpi_aml_user_readable()) 7248c2ecf20Sopenharmony_ci masks |= EPOLLIN | EPOLLRDNORM; 7258c2ecf20Sopenharmony_ci if (acpi_aml_user_writable()) 7268c2ecf20Sopenharmony_ci masks |= EPOLLOUT | EPOLLWRNORM; 7278c2ecf20Sopenharmony_ci 7288c2ecf20Sopenharmony_ci return masks; 7298c2ecf20Sopenharmony_ci} 7308c2ecf20Sopenharmony_ci 7318c2ecf20Sopenharmony_cistatic const struct file_operations acpi_aml_operations = { 7328c2ecf20Sopenharmony_ci .read = acpi_aml_read, 7338c2ecf20Sopenharmony_ci .write = acpi_aml_write, 7348c2ecf20Sopenharmony_ci .poll = acpi_aml_poll, 7358c2ecf20Sopenharmony_ci .open = acpi_aml_open, 7368c2ecf20Sopenharmony_ci .release = acpi_aml_release, 7378c2ecf20Sopenharmony_ci .llseek = generic_file_llseek, 7388c2ecf20Sopenharmony_ci}; 7398c2ecf20Sopenharmony_ci 7408c2ecf20Sopenharmony_cistatic const struct acpi_debugger_ops acpi_aml_debugger = { 7418c2ecf20Sopenharmony_ci .create_thread = acpi_aml_create_thread, 7428c2ecf20Sopenharmony_ci .read_cmd = acpi_aml_read_cmd, 7438c2ecf20Sopenharmony_ci .write_log = acpi_aml_write_log, 7448c2ecf20Sopenharmony_ci .wait_command_ready = acpi_aml_wait_command_ready, 7458c2ecf20Sopenharmony_ci .notify_command_complete = acpi_aml_notify_command_complete, 7468c2ecf20Sopenharmony_ci}; 7478c2ecf20Sopenharmony_ci 7488c2ecf20Sopenharmony_cistatic int __init acpi_aml_init(void) 7498c2ecf20Sopenharmony_ci{ 7508c2ecf20Sopenharmony_ci int ret; 7518c2ecf20Sopenharmony_ci 7528c2ecf20Sopenharmony_ci if (acpi_disabled) 7538c2ecf20Sopenharmony_ci return -ENODEV; 7548c2ecf20Sopenharmony_ci 7558c2ecf20Sopenharmony_ci /* Initialize AML IO interface */ 7568c2ecf20Sopenharmony_ci mutex_init(&acpi_aml_io.lock); 7578c2ecf20Sopenharmony_ci init_waitqueue_head(&acpi_aml_io.wait); 7588c2ecf20Sopenharmony_ci acpi_aml_io.out_crc.buf = acpi_aml_io.out_buf; 7598c2ecf20Sopenharmony_ci acpi_aml_io.in_crc.buf = acpi_aml_io.in_buf; 7608c2ecf20Sopenharmony_ci 7618c2ecf20Sopenharmony_ci acpi_aml_dentry = debugfs_create_file("acpidbg", 7628c2ecf20Sopenharmony_ci S_IFREG | S_IRUGO | S_IWUSR, 7638c2ecf20Sopenharmony_ci acpi_debugfs_dir, NULL, 7648c2ecf20Sopenharmony_ci &acpi_aml_operations); 7658c2ecf20Sopenharmony_ci 7668c2ecf20Sopenharmony_ci ret = acpi_register_debugger(THIS_MODULE, &acpi_aml_debugger); 7678c2ecf20Sopenharmony_ci if (ret) { 7688c2ecf20Sopenharmony_ci debugfs_remove(acpi_aml_dentry); 7698c2ecf20Sopenharmony_ci acpi_aml_dentry = NULL; 7708c2ecf20Sopenharmony_ci return ret; 7718c2ecf20Sopenharmony_ci } 7728c2ecf20Sopenharmony_ci 7738c2ecf20Sopenharmony_ci acpi_aml_initialized = true; 7748c2ecf20Sopenharmony_ci return 0; 7758c2ecf20Sopenharmony_ci} 7768c2ecf20Sopenharmony_ci 7778c2ecf20Sopenharmony_cistatic void __exit acpi_aml_exit(void) 7788c2ecf20Sopenharmony_ci{ 7798c2ecf20Sopenharmony_ci if (acpi_aml_initialized) { 7808c2ecf20Sopenharmony_ci acpi_unregister_debugger(&acpi_aml_debugger); 7818c2ecf20Sopenharmony_ci debugfs_remove(acpi_aml_dentry); 7828c2ecf20Sopenharmony_ci acpi_aml_dentry = NULL; 7838c2ecf20Sopenharmony_ci acpi_aml_initialized = false; 7848c2ecf20Sopenharmony_ci } 7858c2ecf20Sopenharmony_ci} 7868c2ecf20Sopenharmony_ci 7878c2ecf20Sopenharmony_cimodule_init(acpi_aml_init); 7888c2ecf20Sopenharmony_cimodule_exit(acpi_aml_exit); 7898c2ecf20Sopenharmony_ci 7908c2ecf20Sopenharmony_ciMODULE_AUTHOR("Lv Zheng"); 7918c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("ACPI debugger userspace IO driver"); 7928c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 793