Re: [PATCH v2] platform/x86: Add driver for LG Gram laptop extra features

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Mon, Sep 24, 2018 at 6:17 PM Matan Ziv-Av <matan@xxxxxxxxxxx> wrote:
> after a long time, but here is my second version of the LG laptop
> support patch.

Thank you for an update.
Unfortunately more work is needed.
My review below.

> The dump of ACPI can be found at http://my.svgalib.org/acpi/ .
>
> From: Matan Ziv-Av <matan@xxxxxxxxxxx>

First of all, please send new version separately with proper commit
message and versioning (hint: -v parameter to git format-patch).

> +Date:          September 2018
> +KernelVersion: 4.19

For sure these will be

October 2018
4.20

> +// SPDX-License-Identifier: GPL-2.0+
> +/*
> + * lg_wmi.c - LG Gram ACPI features and hotkeys Driver
> + *
> + * Copyright (C) 2018 Matan Ziv-Av <matan@xxxxxxxxxxx>

> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *

The idea of SPDX IDs is to get rid of the above boilerplate text.

> + */

> +#include <linux/kernel.h>

> +#include <linux/module.h>
> +#include <linux/init.h>

Should be either one of the above two.

> +#include <linux/types.h>
> +#include <linux/acpi.h>
> +#include <linux/platform_device.h>
> +#include <linux/input.h>
> +#include <linux/input/sparse-keymap.h>

> +#include <acpi/acpi_bus.h>
> +#include <acpi/acpi_drivers.h>

I bet you don't need this two.

> +#include <linux/leds.h>

Keep them sorted.

> +
> +#define LED_DEVICE(_name, max) struct led_classdev _name = { \
> +       .name           = __stringify(_name),   \
> +       .max_brightness = max,                  \
> +       .brightness_set = _name##_set,          \
> +       .brightness_get = _name##_get,          \
> +}

> +#define WMI_EVENT_GUID0        "E4FB94F9-7F2B-4173-AD1A-CD1D95086248"
> +#define WMI_EVENT_GUID1        "023B133E-49D1-4E10-B313-698220140DC2"
> +#define WMI_EVENT_GUID2        "37BE1AC0-C3F2-4B1F-BFBE-8FDEAF2814D6"
> +#define WMI_EVENT_GUID3        "911BAD44-7DF8-4FBB-9319-BABA1C4B293B"
> +#define WMI_METHOD_WMAB "C3A72B38-D3EF-42D3-8CBB-D5A57049F66D"
> +#define WMI_METHOD_WMBB "2B4F501A-BD3C-4394-8DCF-00A7D2BC8210"
> +#define WMI_EVENT_GUID WMI_EVENT_GUID0
> +
> +#define WMAB_METHOD "\\XINI.WMAB"
> +#define WMBB_METHOD "\\XINI.WMBB"
> +#define SB_GGOV_METHOD "\\_SB.GGOV"
> +#define GOV_TLED 0x2020008
> +#define WM_GET 1
> +#define WM_SET 2
> +#define WM_KEY_LIGHT    0x400
> +#define WM_TLED         0x404
> +#define WM_FN_LOCK      0x407
> +#define WM_BATT_LIMIT   0x61
> +#define WM_READER_MODE  0xBF
> +#define WM_FAN_MODE    0x33
> +#define WMBB_BATT_LIMIT 0x10C

Above contains some indentation issues.

> +static const struct key_entry wmi_keymap[] __initconst = {
> +       /* TODO: Add keymap values once found... */
> +       {KE_KEY, 0x70, {KEY_F15} },      /* LG control panel (F1) */
> +       {KE_KEY, 0x74, {KEY_F13} },      /* Touchpad toggle (F5) */
> +       {KE_KEY, 0xf020000, {KEY_F14} }, /* Read mode (F9) */
> +       {KE_KEY, 0x10000000, {KEY_F16} },/* Keyboard backlight (F8) - changes
> +                                         * backlight and sends an event
> +                                         */
> +       {KE_KEY, 0x80, {KEY_RFKILL} },
> +       {KE_END, 0}
> +};
> +
> +static int ggov(u32 arg0)
> +{

> +       args[0].type = ACPI_TYPE_INTEGER;
> +       args[0].integer.value = arg0;
> +
> +       status = acpi_get_handle(NULL, (acpi_string) SB_GGOV_METHOD, &handle);
> +       if (ACPI_FAILURE(status)) {

> +               pr_err("lg_wmi: Cannot get handle");

If you define pr_fmt() you don't need to repeat the name in each message.

> +               return -1;

Consider to use appropriate -ERRNO instead.

> +       }

> +       status = acpi_evaluate_object(handle, NULL, &arg, &buffer);
> +       if (ACPI_FAILURE(status)) {

> +               pr_err("lg_wmi: Call failed.\n");

acpi_handle_err()

> +               return -2;

-ERRNO

> +       }
> +
> +       r = buffer.pointer;
> +       if (r->type != ACPI_TYPE_INTEGER) {
> +               kfree(r);

> +               return -2;

-ERRNO

> +       }
> +
> +       res = r->integer.value;
> +       kfree(r);
> +
> +       return res;
> +}
> +
> +static union acpi_object *lg_wmab(u32 method, u32 arg1, u32 arg2,
> +                                 u32 *retval)
> +{

> +       status = acpi_get_handle(NULL, (acpi_string) WMAB_METHOD, &handle);
> +       if (ACPI_FAILURE(status)) {

> +               pr_err("lg_wmi: Cannot get handle");

pr_fmt()

> +               return NULL;
> +       }

> +       status = acpi_evaluate_object(handle, NULL, &arg, &buffer);
> +       if (ACPI_FAILURE(status)) {

> +               pr_err("lg_wmi: Call failed.\n");

acpi_handle_err();

> +               return NULL;
> +       }
> +
> +       return buffer.pointer;
> +}

> +// ***************************************************************************

Please, drop these fancy delimiter lines.

> +static void wmi_notify(u32 value, void *context)
> +{

> +       pr_debug("event guid %li\n", data);

If you supply some structure which has a pointer to input device, you
may use dev_dbg() and Co for printing in this function.

> +}

> +static void acpi_notify(struct acpi_device *device, u32 event)
> +{
> +       struct key_entry *key;
> +
> +       pr_debug("LGEX0815 notify: %d\n", event);

acpi_handle_dbg()

> +       if (wmi_input_dev) {
> +               key = sparse_keymap_entry_from_scancode(wmi_input_dev, 0x80);
> +               if (key && key->type == KE_KEY)
> +                       sparse_keymap_report_entry(wmi_input_dev, key, 1, true);
> +       }

> +

Redundant blank line.

> +}

> +static ssize_t fan_mode_store(struct device *dev,
> +                                struct device_attribute *attr,
> +                                const char *buffer, size_t count)
> +{
> +       unsigned long value;
> +       union acpi_object *r;
> +       u32 m, v1, v2;
> +       int ret;

> +       ret = kstrtoul(buffer, 10, &value);
> +       if (!ret)
> +               return ret;
> +
> +       if ((value != 0) && (value != 1))
> +               return -EINVAL;

kstrotobool()

> +       m = r->integer.value;
> +       kfree(r);

> +       v1 = (m&0xffffff0f) | (value<<4);
> +       v2 = (m&0xfffffff0) | value;

I don't see why you need to variables for the above, just reuse v as
you did for r.

> +       r = lg_wmab(WM_FAN_MODE, WM_SET, v1, NULL);
> +       kfree(r);
> +       r = lg_wmab(WM_FAN_MODE, WM_SET, v2, NULL);
> +       kfree(r);
> +
> +       return count;
> +}

> +       unsigned int status;
> +

Redundant blank line.

> +       union acpi_object *r;

> +       unsigned int status;
> +

Redundant blank line.

> +       union acpi_object *r;

> +static const struct attribute_group dev_attribute_group = {
> +       .attrs = dev_attributes

Keep comma here.

> +};

> +static enum led_brightness tpad_led_get(struct led_classdev *cdev)
> +{

> +       int r;
> +
> +       r = ggov(GOV_TLED);
> +       return r > 0 ? LED_ON : LED_OFF;

Can be one line.

> +}

> +static void kbd_backlight_set(struct led_classdev *cdev,
> +                       enum led_brightness brightness)
> +{
> +       u32 val;
> +       union acpi_object *r;
> +

> +       val = 0x22;
> +       if (brightness <= LED_OFF)
> +               val = 0;
> +       if (brightness >= LED_FULL)
> +               val = 0x24;

See below.

> +       r = lg_wmab(WM_KEY_LIGHT, WM_SET, val, NULL);
> +       kfree(r);
> +}
> +
> +static enum led_brightness kbd_backlight_get(struct led_classdev *cdev)
> +{
> +       union acpi_object *r;
> +       int val;
> +
> +       r = lg_wmab(WM_KEY_LIGHT, WM_GET, 0, NULL);

> +

Redundant blank line.

> +       if (!r)
> +               return LED_OFF;

> +       switch (r->buffer.pointer[0] & 0x27) {
> +       case 0x24:
> +               val = LED_FULL;
> +               break;
> +       case 0x22:
> +               val = LED_HALF;
> +               break;
> +       default:
> +               val = LED_OFF;
> +       }

Looking into this values it looks like BIT(5) defines on/off (or
enabled/disabled) state and lowest three bits defines the value of
brightness.
Correct?

If so, can it be rewritten taking above into account?

> +
> +       kfree(r);
> +
> +       return val;
> +}

> +static struct platform_driver pf_driver = {
> +       .driver = {
> +                  .name = PLATFORM_NAME,
> +       }
> +};
> +
> +static int acpi_add(struct acpi_device *device)
> +{

> +       int ret = 0;

Redundant assignment.

> +       /* don't run again if already initialized */
> +       if (atomic_add_return(1, &pf_users) > 1)
> +               return 0;

Oh, please use just a pf_device for that. Either it's NULL or not
which is basically the same.

> +
> +       ret = platform_driver_register(&pf_driver);
> +       if (ret)
> +               goto out;
> +
> +       pf_device = platform_device_register_simple(PLATFORM_NAME,
> +                                                   -1, NULL, 0);

-1 has it's own definition.

> +       if (IS_ERR(pf_device)) {
> +               ret = PTR_ERR(pf_device);
> +               pf_device = NULL;
> +               pr_err("unable to register platform device\n");
> +               goto out_platform_device;
> +       }
> +
> +       ret = sysfs_create_group(&pf_device->dev.kobj, &dev_attribute_group);
> +       if (ret)
> +               goto out_platform_registered;
> +
> +       kbd_backlight_registered = !led_classdev_register(&pf_device->dev,
> +                                                         &kbd_backlight);

> +       tpad_led_registered =
> +           !led_classdev_register(&pf_device->dev, &tpad_led);

I would rather suggest to provide one variable where bits would define
what parts have been registered. Same for WMI part below.

> +
> +       return 0;

> +}
> +
> +static int acpi_remove(struct acpi_device *device)
> +{

> +       /* deregister only after the last user has gone */
> +       if (!atomic_dec_and_test(&pf_users))
> +               return -EBUSY;

How many of them do you have?

> +}

> +static const struct acpi_device_id device_ids[] = {
> +       {"LGEX0815", 0},
> +       {"", 0},

No comma for terminator line, please.

> +};

> +

Redundant blank line.

> +MODULE_DEVICE_TABLE(acpi, device_ids);
> +
> +static struct acpi_driver acpi_driver = {

> +       .name = "LG",
> +       .class = "LG",

Too short too broad.

> +       .ids = device_ids,
> +       .ops = {
> +               .add = acpi_add,
> +               .remove = acpi_remove,
> +               .notify = acpi_notify,
> +               },
> +       .owner = THIS_MODULE,
> +};


> +static int __init acpi_init(void)
> +{

> +       int result = 0;

Redundant assignment.

> +
> +       result = acpi_bus_register_driver(&acpi_driver);
> +       if (result < 0) {
> +               ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Error registering driver\n"));
> +               return -ENODEV;
> +       }
> +

> +       wmi_inited = !wmi_input_setup();

Hmm... So, it's not a fatal error if WMI is not initialized?

> +
> +       return 0;
> +}

-- 
With Best Regards,
Andy Shevchenko



[Index of Archives]     [Linux Kernel Development]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]

  Powered by Linux