From patchwork Wed Apr 16 01:46:05 2014 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Hui Wang X-Patchwork-Id: 339406 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from huckleberry.canonical.com (huckleberry.canonical.com [91.189.94.19]) by ozlabs.org (Postfix) with ESMTP id 9C697140099; Wed, 16 Apr 2014 11:46:48 +1000 (EST) Received: from localhost ([127.0.0.1] helo=huckleberry.canonical.com) by huckleberry.canonical.com with esmtp (Exim 4.76) (envelope-from ) id 1WaEvv-0006U9-Us; Wed, 16 Apr 2014 01:46:39 +0000 Received: from mail-pa0-f49.google.com ([209.85.220.49]) by huckleberry.canonical.com with esmtp (Exim 4.76) (envelope-from ) id 1WaEvq-0006Ta-Ic for kernel-team@lists.ubuntu.com; Wed, 16 Apr 2014 01:46:34 +0000 Received: by mail-pa0-f49.google.com with SMTP id lj1so10163358pab.8 for ; Tue, 15 Apr 2014 18:46:33 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20130820; h=x-gm-message-state:from:to:cc:subject:date:message-id; bh=KpSreGO95D2l/LRgk+J1NvslkEP0ar7e8f3oDZHxUM4=; b=JD8Zvfieg2mPgYb9113o5KyEDDgwRHXKbx1annWC+BZxlTuykEnnQpXJ6gsWKfSQbE KAvPHS40xCiCISdZQMN3XJgDRETNH6QR8HK3968yzcmdhGcqBwEOkF1su14vwWyRaffL 1fFOMvWAFhx9ix15DBoZQAB3INSC6K1g+CggX1dBNuK6vPWDzovgbYmUO3uwXFubI6jI l3x2NyLkCq2H8rSaK8zI+MCwnZn5naOqkDC/LSL6XisAY6uK6cXysOVT/BfmJ3AoxtTf AUtZXFKP1gSMEKeo7oVuPzVY5CuESVslKuLF60ZaechqFBsmGJ7B/UIXsY0fSt4xT6LL ZlOw== X-Gm-Message-State: ALoCoQmNt1HMvgFGtuVyIkayRDUMEeUrilH+ueBTtd0wz0etDDVWW4iI1qTfgeRf4Ld1GE84bwWr X-Received: by 10.69.25.69 with SMTP id io5mr5629198pbd.22.1397612793553; Tue, 15 Apr 2014 18:46:33 -0700 (PDT) Received: from localhost.localdomain ([116.213.97.190]) by mx.google.com with ESMTPSA id sh5sm43230695pbc.21.2014.04.15.18.46.31 for (version=TLSv1.1 cipher=ECDHE-RSA-RC4-SHA bits=128/128); Tue, 15 Apr 2014 18:46:32 -0700 (PDT) From: Hui Wang To: kernel-team@lists.ubuntu.com Subject: [Trusty PATCH] dell-led: add mic mute led interface Date: Wed, 16 Apr 2014 09:46:05 +0800 Message-Id: <1397612766-9728-1-git-send-email-hui.wang@canonical.com> X-Mailer: git-send-email 1.8.1.2 X-BeenThere: kernel-team@lists.ubuntu.com X-Mailman-Version: 2.1.14 Precedence: list List-Id: Kernel team discussions List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Errors-To: kernel-team-bounces@lists.ubuntu.com Sender: kernel-team-bounces@lists.ubuntu.com From: Alex Hung BugLink: http://bugs.launchpad.net/bugs/1308297 commit de8021d313eb0dd4aab3cb4cad3224703970be1b of linux-leds This patch provides similar led functional of 420f973 thinkpad-acpi: Add mute and mic-mute LED functionality Signed-off-by: Alex Hung Signed-off-by: Bryan Wu Signed-off-by: Hui Wang --- This patch has already been accepted by linux-leds repository and is going to be merged by upstream linux-3.16. But we have an OEM preload project to ask for this feature, so I try to backport it to ubuntu kernel before it shows up in the upstream. drivers/leds/dell-led.c | 171 +++++++++++++++++++++++++++++++++++++++++++++-- include/linux/dell-led.h | 10 +++ 2 files changed, 174 insertions(+), 7 deletions(-) create mode 100644 include/linux/dell-led.h diff --git a/drivers/leds/dell-led.c b/drivers/leds/dell-led.c index e5c5738..c36acaf 100644 --- a/drivers/leds/dell-led.c +++ b/drivers/leds/dell-led.c @@ -15,12 +15,15 @@ #include #include #include +#include +#include MODULE_AUTHOR("Louis Davis/Jim Dailey"); MODULE_DESCRIPTION("Dell LED Control Driver"); MODULE_LICENSE("GPL"); #define DELL_LED_BIOS_GUID "F6E4FE6E-909D-47cb-8BAB-C9F6F2F8D396" +#define DELL_APP_GUID "A80593CE-A997-11DA-B012-B622A1EF5492" MODULE_ALIAS("wmi:" DELL_LED_BIOS_GUID); /* Error Result Codes: */ @@ -39,6 +42,149 @@ MODULE_ALIAS("wmi:" DELL_LED_BIOS_GUID); #define CMD_LED_OFF 17 #define CMD_LED_BLINK 18 +struct app_wmi_args { + u16 class; + u16 selector; + u32 arg1; + u32 arg2; + u32 arg3; + u32 arg4; + u32 res1; + u32 res2; + u32 res3; + u32 res4; + char dummy[92]; +}; + +#define GLOBAL_MIC_MUTE_ENABLE 0x364 +#define GLOBAL_MIC_MUTE_DISABLE 0x365 + +struct dell_bios_data_token { + u16 tokenid; + u16 location; + u16 value; +}; + +struct __attribute__ ((__packed__)) dell_bios_calling_interface { + struct dmi_header header; + u16 cmd_io_addr; + u8 cmd_io_code; + u32 supported_cmds; + struct dell_bios_data_token damap[]; +}; + +static struct dell_bios_data_token dell_mic_tokens[2]; + +static int dell_wmi_perform_query(struct app_wmi_args *args) +{ + struct app_wmi_args *bios_return; + union acpi_object *obj; + struct acpi_buffer input; + struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL }; + acpi_status status; + u32 rc = -EINVAL; + + input.length = 128; + input.pointer = args; + + status = wmi_evaluate_method(DELL_APP_GUID, 0, 1, &input, &output); + if (!ACPI_SUCCESS(status)) + goto err_out0; + + obj = output.pointer; + if (!obj) + goto err_out0; + + if (obj->type != ACPI_TYPE_BUFFER) + goto err_out1; + + bios_return = (struct app_wmi_args *)obj->buffer.pointer; + rc = bios_return->res1; + if (rc) + goto err_out1; + + memcpy(args, bios_return, sizeof(struct app_wmi_args)); + rc = 0; + + err_out1: + kfree(obj); + err_out0: + return rc; +} + +static void __init find_micmute_tokens(const struct dmi_header *dm, void *dummy) +{ + struct dell_bios_calling_interface *calling_interface; + struct dell_bios_data_token *token; + int token_size = sizeof(struct dell_bios_data_token); + int i = 0; + + if (dm->type == 0xda && dm->length > 17) { + calling_interface = container_of(dm, + struct dell_bios_calling_interface, header); + + token = &calling_interface->damap[i]; + while (token->tokenid != 0xffff) { + if (token->tokenid == GLOBAL_MIC_MUTE_DISABLE) + memcpy(&dell_mic_tokens[0], token, token_size); + else if (token->tokenid == GLOBAL_MIC_MUTE_ENABLE) + memcpy(&dell_mic_tokens[1], token, token_size); + + i++; + token = &calling_interface->damap[i]; + } + } +} + +static int dell_micmute_led_set(int state) +{ + struct app_wmi_args args; + struct dell_bios_data_token *token; + + if (!wmi_has_guid(DELL_APP_GUID)) + return -ENODEV; + + if (state == 0 || state == 1) + token = &dell_mic_tokens[state]; + else + return -EINVAL; + + memset(&args, 0, sizeof(struct app_wmi_args)); + + args.class = 1; + args.arg1 = token->location; + args.arg2 = token->value; + + dell_wmi_perform_query(&args); + + return state; +} + +int dell_app_wmi_led_set(int whichled, int on) +{ + int state = 0; + + switch (whichled) { + case DELL_LED_MICMUTE: + state = dell_micmute_led_set(on); + break; + default: + pr_warn("led type %x is not supported\n", whichled); + break; + } + + return state; +} +EXPORT_SYMBOL_GPL(dell_app_wmi_led_set); + +static int __init dell_micmute_led_init(void) +{ + memset(dell_mic_tokens, 0, sizeof(struct dell_bios_data_token) * 2); + dmi_walk(find_micmute_tokens, NULL); + + return 0; +} + struct bios_args { unsigned char length; unsigned char result_code; @@ -181,21 +327,32 @@ static int __init dell_led_init(void) { int error = 0; - if (!wmi_has_guid(DELL_LED_BIOS_GUID)) + if (!wmi_has_guid(DELL_LED_BIOS_GUID) && !wmi_has_guid(DELL_APP_GUID)) return -ENODEV; - error = led_off(); - if (error != 0) - return -ENODEV; + if (wmi_has_guid(DELL_APP_GUID)) + error = dell_micmute_led_init(); - return led_classdev_register(NULL, &dell_led); + if (wmi_has_guid(DELL_LED_BIOS_GUID)) { + error = led_off(); + if (error != 0) + return -ENODEV; + + error = led_classdev_register(NULL, &dell_led); + } + + return error; } static void __exit dell_led_exit(void) { - led_classdev_unregister(&dell_led); + int error = 0; - led_off(); + if (wmi_has_guid(DELL_LED_BIOS_GUID)) { + error = led_off(); + if (error == 0) + led_classdev_unregister(&dell_led); + } } module_init(dell_led_init); diff --git a/include/linux/dell-led.h b/include/linux/dell-led.h new file mode 100644 index 0000000..7009b8b --- /dev/null +++ b/include/linux/dell-led.h @@ -0,0 +1,10 @@ +#ifndef __DELL_LED_H__ +#define __DELL_LED_H__ + +enum { + DELL_LED_MICMUTE, +}; + +int dell_app_wmi_led_set(int whichled, int on); + +#endif