diff options
Diffstat (limited to 'drivers/acpi/ec.c')
| -rw-r--r-- | drivers/acpi/ec.c | 86 | 
1 files changed, 60 insertions, 26 deletions
diff --git a/drivers/acpi/ec.c b/drivers/acpi/ec.c index 9d4761d2f6b7..2614a839c60d 100644 --- a/drivers/acpi/ec.c +++ b/drivers/acpi/ec.c @@ -22,10 +22,6 @@   *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU   *  General Public License for more details.   * - *  You should have received a copy of the GNU General Public License along - *  with this program; if not, write to the Free Software Foundation, Inc., - *  59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. - *   * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~   */ @@ -165,8 +161,16 @@ struct transaction {  	u8 flags;  }; +struct acpi_ec_query { +	struct transaction transaction; +	struct work_struct work; +	struct acpi_ec_query_handler *handler; +}; +  static int acpi_ec_query(struct acpi_ec *ec, u8 *data);  static void advance_transaction(struct acpi_ec *ec); +static void acpi_ec_event_handler(struct work_struct *work); +static void acpi_ec_event_processor(struct work_struct *work);  struct acpi_ec *boot_ec, *first_ec;  EXPORT_SYMBOL(first_ec); @@ -978,60 +982,90 @@ void acpi_ec_remove_query_handler(struct acpi_ec *ec, u8 query_bit)  }  EXPORT_SYMBOL_GPL(acpi_ec_remove_query_handler); -static void acpi_ec_run(void *cxt) +static struct acpi_ec_query *acpi_ec_create_query(u8 *pval)  { -	struct acpi_ec_query_handler *handler = cxt; +	struct acpi_ec_query *q; +	struct transaction *t; + +	q = kzalloc(sizeof (struct acpi_ec_query), GFP_KERNEL); +	if (!q) +		return NULL; +	INIT_WORK(&q->work, acpi_ec_event_processor); +	t = &q->transaction; +	t->command = ACPI_EC_COMMAND_QUERY; +	t->rdata = pval; +	t->rlen = 1; +	return q; +} + +static void acpi_ec_delete_query(struct acpi_ec_query *q) +{ +	if (q) { +		if (q->handler) +			acpi_ec_put_query_handler(q->handler); +		kfree(q); +	} +} + +static void acpi_ec_event_processor(struct work_struct *work) +{ +	struct acpi_ec_query *q = container_of(work, struct acpi_ec_query, work); +	struct acpi_ec_query_handler *handler = q->handler; -	if (!handler) -		return;  	ec_dbg_evt("Query(0x%02x) started", handler->query_bit);  	if (handler->func)  		handler->func(handler->data);  	else if (handler->handle)  		acpi_evaluate_object(handler->handle, NULL, NULL, NULL);  	ec_dbg_evt("Query(0x%02x) stopped", handler->query_bit); -	acpi_ec_put_query_handler(handler); +	acpi_ec_delete_query(q);  }  static int acpi_ec_query(struct acpi_ec *ec, u8 *data)  {  	u8 value = 0;  	int result; -	acpi_status status;  	struct acpi_ec_query_handler *handler; -	struct transaction t = {.command = ACPI_EC_COMMAND_QUERY, -				.wdata = NULL, .rdata = &value, -				.wlen = 0, .rlen = 1}; +	struct acpi_ec_query *q; + +	q = acpi_ec_create_query(&value); +	if (!q) +		return -ENOMEM;  	/*  	 * Query the EC to find out which _Qxx method we need to evaluate.  	 * Note that successful completion of the query causes the ACPI_EC_SCI  	 * bit to be cleared (and thus clearing the interrupt source).  	 */ -	result = acpi_ec_transaction(ec, &t); -	if (result) -		return result; -	if (data) -		*data = value; +	result = acpi_ec_transaction(ec, &q->transaction);  	if (!value) -		return -ENODATA; +		result = -ENODATA; +	if (result) +		goto err_exit;  	mutex_lock(&ec->mutex);  	list_for_each_entry(handler, &ec->list, node) {  		if (value == handler->query_bit) { -			/* have custom handler for this bit */ -			handler = acpi_ec_get_query_handler(handler); +			q->handler = acpi_ec_get_query_handler(handler);  			ec_dbg_evt("Query(0x%02x) scheduled", -				   handler->query_bit); -			status = acpi_os_execute((handler->func) ? -				OSL_NOTIFY_HANDLER : OSL_GPE_HANDLER, -				acpi_ec_run, handler); -			if (ACPI_FAILURE(status)) +				   q->handler->query_bit); +			/* +			 * It is reported that _Qxx are evaluated in a +			 * parallel way on Windows: +			 * https://bugzilla.kernel.org/show_bug.cgi?id=94411 +			 */ +			if (!schedule_work(&q->work))  				result = -EBUSY;  			break;  		}  	}  	mutex_unlock(&ec->mutex); + +err_exit: +	if (result && q) +		acpi_ec_delete_query(q); +	if (data) +		*data = value;  	return result;  }  |