Re: [RFC][PATCH] New ACPI-WMI driver for shuttle machines

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

 



On Tue, Nov 30, 2010 at 6:27 PM, Herton Ronaldo Krzesinski
<herton@xxxxxxxxxxxxxxx> wrote:
> Hi,
>
> Recently, I received some shuttle machines that are mostly a notebook "glued"
> to a desktop form factor. They are notebook hardware, have a webcam, wireless,
> etc., but they don't have a notebook keyboard, and because of this no way to
> control the hardware, like webcam on/off, etc. So, a driver is needed.
>
> The machines I have here is similar (not same) to this on shuttle website:
> http://us.shuttle.com/X50v2.aspx
>
> What follows is a patch which adds a driver which exposes the controls
> available through ACPI-WMI. For now just a RFC to get feedback. Also, on some
> shuttle machines, fn+<function> can have different values, and I need to
> implement a more flexible way to change command numbers (fn+n) depending on
> model of shuttle machine, so it isn't a final version yet. And on Shuttle
> DA18IE, the interface for video switch isn't final and could change, which
> may affect the driver.
>
> [PATCH] Add shuttle-wmi driver
>
> This adds a new platform driver for shuttle machines. On some desktop
> machines, shuttle is using laptop hardware, but without laptop keyboard.
> Thus, for some features like turn on/off webcam, a way is need to
> control the hardware. The driver uses ACPI-WMI interface provided to
> export available controls through sysfs.
>
> Signed-off-by: Herton Ronaldo Krzesinski <herton@xxxxxxxxxxxxxxx>
> ---
> Â.../ABI/testing/sysfs-platform-shuttle-wmi     | Â161 ++++
> Âdrivers/platform/x86/Kconfig            |  16 +
> Âdrivers/platform/x86/Makefile           Â|  Â1 +
> Âdrivers/platform/x86/shuttle-wmi.c         | Â843 ++++++++++++++++++++
> Â4 files changed, 1021 insertions(+), 0 deletions(-)
> Âcreate mode 100644 Documentation/ABI/testing/sysfs-platform-shuttle-wmi
> Âcreate mode 100644 drivers/platform/x86/shuttle-wmi.c
>
> diff --git a/Documentation/ABI/testing/sysfs-platform-shuttle-wmi b/Documentation/ABI/testing/sysfs-platform-shuttle-wmi
> new file mode 100644
> index 0000000..63a8c8b
> --- /dev/null
> +++ b/Documentation/ABI/testing/sysfs-platform-shuttle-wmi
> @@ -0,0 +1,161 @@
> +What: Â Â Â Â Â/sys/devices/platform/shuttle_wmi/brightness_down
> +Date: Â Â Â Â ÂNovember 2010
> +KernelVersion: 2.6.37
> +Contact: Â Â Â "Herton Ronaldo Krzesinski" <herton@xxxxxxxxxxxxxxx>
> +Description:
> + Â Â Â Â Â Â Â This is a write only option (accepts any single value, eg.
> + Â Â Â Â Â Â Â "echo 1 > brightness_down") that is equivalent of pressing
> + Â Â Â Â Â Â Â fn+<brightness down> function on notebooks. This option exists
> + Â Â Â Â Â Â Â because of shuttle machines that are notebooks in desktop form
> + Â Â Â Â Â Â Â factor, and which don't have the notebook keyboard, thus no
> + Â Â Â Â Â Â Â way to use fn+<brightness down>.
> +
> +What: Â Â Â Â Â/sys/devices/platform/shuttle_wmi/brightness_up
> +Date: Â Â Â Â ÂNovember 2010
> +KernelVersion: 2.6.37
> +Contact: Â Â Â "Herton Ronaldo Krzesinski" <herton@xxxxxxxxxxxxxxx>
> +Description:
> + Â Â Â Â Â Â Â This is a write only option (accepts any single value, eg.
> + Â Â Â Â Â Â Â "echo 1 > brightness_up") that is equivalent of pressing
> + Â Â Â Â Â Â Â fn+<brightness up> function on notebooks. This option exists
> + Â Â Â Â Â Â Â because of shuttle machines that are notebooks in desktop form
> + Â Â Â Â Â Â Â factor, and which don't have the notebook keyboard, thus no
> + Â Â Â Â Â Â Â way to use fn+<brightness up>.

Why such files ? Can't we do the same using the backlight device ?

> +What: Â Â Â Â Â/sys/devices/platform/shuttle_wmi/cut_lvds
> +Date: Â Â Â Â ÂNovember 2010
> +KernelVersion: 2.6.37
> +Contact: Â Â Â "Herton Ronaldo Krzesinski" <herton@xxxxxxxxxxxxxxx>
> +Description:
> + Â Â Â Â Â Â Â This is a write only option. Writing any single non-zero value
> + Â Â Â Â Â Â Â to it enables main screen output, 0 to disable.

Same, could be handled by the backlight device.

> +What: Â Â Â Â Â/sys/devices/platform/shuttle_wmi/lcd_auto_adjust
> +Date: Â Â Â Â ÂNovember 2010
> +KernelVersion: 2.6.37
> +Contact: Â Â Â "Herton Ronaldo Krzesinski" <herton@xxxxxxxxxxxxxxx>
> +Description:
> + Â Â Â Â Â Â Â This is a write only option (accepts any single value, eg.
> + Â Â Â Â Â Â Â "echo 1 > lcd_auto_adjust") that starts LCD auto-adjust
> + Â Â Â Â Â Â Â function. Some shuttle machines have LCD attached to analog
> + Â Â Â Â Â Â Â VGA connector, so uses/needs auto-adjust.
> +
> +What: Â Â Â Â Â/sys/devices/platform/shuttle_wmi/lbar_brightness_down
> +Date: Â Â Â Â ÂNovember 2010
> +KernelVersion: 2.6.37
> +Contact: Â Â Â "Herton Ronaldo Krzesinski" <herton@xxxxxxxxxxxxxxx>
> +Description:
> + Â Â Â Â Â Â Â This is a write only option (accepts any single value, eg.
> + Â Â Â Â Â Â Â "echo 1 > lbar_brightness_down"). Decreases one step of lightbar
> + Â Â Â Â Â Â Â brightness.
> +
> +What: Â Â Â Â Â/sys/devices/platform/shuttle_wmi/lbar_brightness_up
> +Date: Â Â Â Â ÂNovember 2010
> +KernelVersion: 2.6.37
> +Contact: Â Â Â "Herton Ronaldo Krzesinski" <herton@xxxxxxxxxxxxxxx>
> +Description:
> + Â Â Â Â Â Â Â This is a write only option (accepts any single value, eg.
> + Â Â Â Â Â Â Â "echo 1 > lbar_brightness_up"). Increases one step of lightbar
> + Â Â Â Â Â Â Â brightness.

What is the lightbar exactly ? Some kind of led ? Can't you use the
led class instead ?
Also the driver seems to reference keyboard brightness, if available,
this should be
implemented as a led named shuttle::kbd_backlight.

> +What: Â Â Â Â Â/sys/devices/platform/shuttle_wmi/model_name
> +Date: Â Â Â Â ÂNovember 2010
> +KernelVersion: 2.6.37
> +Contact: Â Â Â "Herton Ronaldo Krzesinski" <herton@xxxxxxxxxxxxxxx>
> +Description:
> + Â Â Â Â Â Â Â This is a read only attribute which outputs a string with model
> + Â Â Â Â Â Â Â name of the machine. When shuttle-wmi can't determine which
> + Â Â Â Â Â Â Â model it is, "Unknown" is returned. Otherwise, the possible
> + Â Â Â Â Â Â Â models are "Shuttle MA", "Shuttle DA18IE" or "Shuttle DA18IM".
> +
> +What: Â Â Â Â Â/sys/devices/platform/shuttle_wmi/panel_set_default
> +Date: Â Â Â Â ÂNovember 2010
> +KernelVersion: 2.6.37
> +Contact: Â Â Â "Herton Ronaldo Krzesinski" <herton@xxxxxxxxxxxxxxx>
> +Description:
> + Â Â Â Â Â Â Â This is a write only option (accepts any single value, eg.
> + Â Â Â Â Â Â Â "echo 1 > panel_set_default"). Probably resets panel/lcd to
> + Â Â Â Â Â Â Â default configuration, function not explained in shuttle wmi
> + Â Â Â Â Â Â Â documentation.
> +
> +What: Â Â Â Â Â/sys/devices/platform/shuttle_wmi/powersave
> +Date: Â Â Â Â ÂNovember 2010
> +KernelVersion: 2.6.37
> +Contact: Â Â Â "Herton Ronaldo Krzesinski" <herton@xxxxxxxxxxxxxxx>
> +Description:
> + Â Â Â Â Â Â Â Control powersave state. 1 means on, 0 means off.
> + Â Â Â Â Â Â Â When enabled, it basically forces the cpu to stay on powersave
> + Â Â Â Â Â Â Â state (similar to powersave governor in cpufreq) when machine is
> + Â Â Â Â Â Â Â only running on battery. If not running on battery, this
> + Â Â Â Â Â Â Â function isn't expected to work, any attempt to enable this
> + Â Â Â Â Â Â Â returns -EIO. The function is equal to fn+<cpu> switch on some
> + Â Â Â Â Â Â Â notebooks.
> +
> +What: Â Â Â Â Â/sys/devices/platform/shuttle_wmi/sleep
> +Date: Â Â Â Â ÂNovember 2010
> +KernelVersion: 2.6.37
> +Contact: Â Â Â "Herton Ronaldo Krzesinski" <herton@xxxxxxxxxxxxxxx>
> +Description:
> + Â Â Â Â Â Â Â This is a write only option (accepts any single value, eg.
> + Â Â Â Â Â Â Â "echo 1 > sleep") that is equivalent of pressing fn+<sleep>
> + Â Â Â Â Â Â Â function on notebooks.
> +
> +What: Â Â Â Â Â/sys/devices/platform/shuttle_wmi/sound_mute
> +Date: Â Â Â Â ÂNovember 2010
> +KernelVersion: 2.6.37
> +Contact: Â Â Â "Herton Ronaldo Krzesinski" <herton@xxxxxxxxxxxxxxx>
> +Description:
> + Â Â Â Â Â Â Â This is a write only option (accepts any single value, eg.
> + Â Â Â Â Â Â Â "echo 1 > sound_mute") that is equivalent of pressing fn+<mute>
> + Â Â Â Â Â Â Â function on notebooks.
> +
> +What: Â Â Â Â Â/sys/devices/platform/shuttle_wmi/switch_video
> +Date: Â Â Â Â ÂNovember 2010
> +KernelVersion: 2.6.37
> +Contact: Â Â Â "Herton Ronaldo Krzesinski" <herton@xxxxxxxxxxxxxxx>
> +Description:
> + Â Â Â Â Â Â Â This is a write only option (accepts any single value, eg.
> + Â Â Â Â Â Â Â "echo 1 > switch_video") that is equivalent of pressing
> + Â Â Â Â Â Â Â fn+<display switch> function on notebooks.
> +
> +What: Â Â Â Â Â/sys/devices/platform/shuttle_wmi/touchpad
> +Date: Â Â Â Â ÂNovember 2010
> +KernelVersion: 2.6.37
> +Contact: Â Â Â "Herton Ronaldo Krzesinski" <herton@xxxxxxxxxxxxxxx>
> +Description:
> + Â Â Â Â Â Â Â Control touchpad state. 1 means on, 0 means off.
> +
> +What: Â Â Â Â Â/sys/devices/platform/shuttle_wmi/volume_down
> +Date: Â Â Â Â ÂNovember 2010
> +KernelVersion: 2.6.37
> +Contact: Â Â Â "Herton Ronaldo Krzesinski" <herton@xxxxxxxxxxxxxxx>
> +Description:
> + Â Â Â Â Â Â Â This is a write only option (accepts any single value, eg.
> + Â Â Â Â Â Â Â "echo 1 > volume_down") that is equivalent of pressing
> + Â Â Â Â Â Â Â fn+<volume down> function on notebooks.
> +
> +What: Â Â Â Â Â/sys/devices/platform/shuttle_wmi/volume_up
> +Date: Â Â Â Â ÂNovember 2010
> +KernelVersion: 2.6.37
> +Contact: Â Â Â "Herton Ronaldo Krzesinski" <herton@xxxxxxxxxxxxxxx>
> +Description:
> + Â Â Â Â Â Â Â This is a write only option (accepts any single value, eg.
> + Â Â Â Â Â Â Â "echo 1 > volume_up") that is equivalent of pressing
> + Â Â Â Â Â Â Â fn+<volume up> function on notebooks.

Are volume_down and volume_up really needed ? can't alsamixer do the same ?

> +What: Â Â Â Â Â/sys/devices/platform/shuttle_wmi/webcam
> +Date: Â Â Â Â ÂNovember 2010
> +KernelVersion: 2.6.37
> +Contact: Â Â Â "Herton Ronaldo Krzesinski" <herton@xxxxxxxxxxxxxxx>
> +Description:
> + Â Â Â Â Â Â Â Control webcam state. 1 means on, 0 means off.
> +
> +What: Â Â Â Â Â/sys/devices/platform/shuttle_wmi/white_balance
> +Date: Â Â Â Â ÂNovember 2010
> +KernelVersion: 2.6.37
> +Contact: Â Â Â "Herton Ronaldo Krzesinski" <herton@xxxxxxxxxxxxxxx>
> +Description:
> + Â Â Â Â Â Â Â This is a write only option (accepts any single value, eg.
> + Â Â Â Â Â Â Â "echo 1 > white_balance"). Probably triggers an automatic
> + Â Â Â Â Â Â Â white balance adjustment for lcd, function not explained in
> + Â Â Â Â Â Â Â shuttle wmi documentation.


Here, I don't really understand why you reference "fn+<keys>". We
don't really care about the keys right ? What we want is make the feature
available for the user.

Some of the files likes volume_up/volume_down seems to be here for
debug purpose. Maybe you could add a simple debugfs interface
to send custom commands to the wmi device.

> diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig
> index faec777..ef84a4d 100644
> --- a/drivers/platform/x86/Kconfig
> +++ b/drivers/platform/x86/Kconfig
> @@ -639,4 +639,20 @@ config XO1_RFKILL
> Â Â Â Â ÂSupport for enabling/disabling the WLAN interface on the OLPC XO-1
> Â Â Â Â Âlaptop.
>
> +config SHUTTLE_WMI
> + Â Â Â tristate "Shuttle WMI Extras"
> + Â Â Â depends on ACPI
> + Â Â Â depends on BACKLIGHT_CLASS_DEVICE
> + Â Â Â depends on RFKILL
> + Â Â Â depends on INPUT
> + Â Â Â select ACPI_WMI
> + Â Â Â select INPUT_SPARSEKMAP
> + Â Â Â ---help---
> + Â Â Â Â This is a driver for Shuttle machines (mainly for laptops in desktop
> + Â Â Â Â form factor). It adds controls for wireless, bluetooth, and 3g control
> + Â Â Â Â radios, webcam switch, backlight controls, among others.
> +
> + Â Â Â Â If you have an Shuttle machine with ACPI-WMI interface say Y or M
> + Â Â Â Â here.
> +

Add some DA18IE/DA18IM/MA ref here ?

> Âendif # X86_PLATFORM_DEVICES
> diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile
> index 9950ccc..6a8fa82 100644
> --- a/drivers/platform/x86/Makefile
> +++ b/drivers/platform/x86/Makefile
> @@ -33,3 +33,4 @@ obj-$(CONFIG_INTEL_IPS) Â Â Â Â Â Â Â += intel_ips.o
> Âobj-$(CONFIG_GPIO_INTEL_PMIC) Â+= intel_pmic_gpio.o
> Âobj-$(CONFIG_XO1_RFKILL) Â Â Â += xo1-rfkill.o
> Âobj-$(CONFIG_IBM_RTL) Â Â Â Â Â+= ibm_rtl.o
> +obj-$(CONFIG_SHUTTLE_WMI) Â Â Â+= shuttle-wmi.o
> diff --git a/drivers/platform/x86/shuttle-wmi.c b/drivers/platform/x86/shuttle-wmi.c
> new file mode 100644
> index 0000000..389a16d
> --- /dev/null
> +++ b/drivers/platform/x86/shuttle-wmi.c
> @@ -0,0 +1,843 @@
> +/*
> + * ACPI-WMI driver for Shuttle DA18IE/DA18IM/MA
> + *
> + * Copyright (c) 2009 Herton Ronaldo Krzesinski <herton@xxxxxxxxxxxxxxx>

2010 ?

> + * Development of this driver was funded by Positivo Informatica S.A.
> + * Parts of the driver were based on some WMI documentation provided by Shuttle
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + */
> +
> +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
> +
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/init.h>
> +#include <linux/types.h>
> +#include <linux/acpi.h>
> +#include <linux/platform_device.h>
> +#include <linux/rfkill.h>
> +#include <linux/input.h>
> +#include <linux/input/sparse-keymap.h>
> +#include <linux/backlight.h>
> +#include <linux/fb.h>
> +
> +MODULE_AUTHOR("Herton Ronaldo Krzesinski");
> +MODULE_DESCRIPTION("Shuttle DA18IE/DA18IM/MA WMI Extras Driver");
> +MODULE_LICENSE("GPL");
> +
> +#define SHUTTLE_WMI_SETGET_GUID "abbc0f6f-8ea1-11d1-00a0-c90629100000"
> +#define SHUTTLE_WMI_EVENT_GUID "abbc0f72-8ea1-11d1-00a0-c90629100000"
> +MODULE_ALIAS("wmi:"SHUTTLE_WMI_SETGET_GUID);
> +MODULE_ALIAS("wmi:"SHUTTLE_WMI_EVENT_GUID);
> +
> +#define CMD_WRITEEC Â Â 0x00
> +#define CMD_READEC Â Â Â0x01
> +#define CMD_SCMD Â Â Â Â0x02
> +#define CMD_INT15 Â Â Â 0x03
> +#define CMD_HWSW Â Â Â Â0x07
> +#define CMD_LCTRL Â Â Â 0x09
> +#define CMD_CUTLVDS Â Â 0x11
> +#define CMD_MA Â Â Â Â Â0x18
> +#define CMD_DA18IE Â Â Â0x19
> +#define CMD_DA18IM Â Â Â0x20
> +
> +#define ECRAM_ER0 Â Â Â 0x443
> +#define ECRAM_ER1 Â Â Â 0x47b
> +#define ECRAM_ER2 Â Â Â 0x758
> +
> +struct shuttle_id {
> + Â Â Â unsigned char cmd_id;
> + Â Â Â char *model_name;
> + Â Â Â bool step_brightness;
> +};
> +
> +static struct shuttle_id shuttle_ids[] = {
> + Â Â Â { CMD_MA, "Shuttle MA", false },
> + Â Â Â { CMD_DA18IE, "Shuttle DA18IE", true },
> + Â Â Â { CMD_DA18IM, "Shuttle DA18IM", false }
> +};
> +
> +struct shuttle_wmi_priv {
> + Â Â Â struct platform_device *pdev;
> + Â Â Â struct input_dev *inputdev;
> + Â Â Â struct shuttle_id *id;
> + Â Â Â struct backlight_device *bd;
> +};

I would only name it struct shuttle_wmi, but not very important.

> +struct shuttle_cmd {
> + Â Â Â u16 param2;
> + Â Â Â u16 param1;
> + Â Â Â u8 arg;
> + Â Â Â u8 cmd;
> + Â Â Â u16 hdr;
> +};
> +
> +static acpi_status wmi_setget_mtd(struct shuttle_cmd *scmd, u32 **res)
> +{
> + Â Â Â acpi_status status;
> + Â Â Â union acpi_object *obj;
> + Â Â Â struct acpi_buffer input;
> + Â Â Â static DEFINE_MUTEX(mtd_lock);
> + Â Â Â struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
> +
> + Â Â Â input.length = sizeof(struct shuttle_cmd);
> + Â Â Â scmd->hdr = 0xec00;
> + Â Â Â input.pointer = (u8 *) scmd;
> +
> + Â Â Â mutex_lock(&mtd_lock);
> + Â Â Â status = wmi_evaluate_method(SHUTTLE_WMI_SETGET_GUID, 0, 2,
> + Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â&input, &output);
> + Â Â Â mutex_unlock(&mtd_lock);

I'm not sure why you need a mutex here ?

> + Â Â Â if (ACPI_FAILURE(status))
> + Â Â Â Â Â Â Â return status;
> +
> + Â Â Â obj = output.pointer;
> + Â Â Â if (obj) {
> + Â Â Â Â Â Â Â if (obj->type == ACPI_TYPE_INTEGER) {
> + Â Â Â Â Â Â Â Â Â Â Â if (*res)
> + Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â **res = obj->integer.value;
> + Â Â Â Â Â Â Â } else
> + Â Â Â Â Â Â Â Â Â Â Â pr_err("Unsupported object returned (%s)", __func__);
> + Â Â Â Â Â Â Â kfree(obj);
> + Â Â Â } else {
> + Â Â Â Â Â Â Â if (*res) {
> + Â Â Â Â Â Â Â Â Â Â Â pr_warning("No result from WMI method (%s)", __func__);
> + Â Â Â Â Â Â Â Â Â Â Â *res = NULL;
> + Â Â Â Â Â Â Â }
> + Â Â Â }
> + Â Â Â return AE_OK;
> +}
>

wmi_setget_mtd would probably be simpler like that:

static int wmi_setget_mtd(struct shuttle_cmd *scmd, u32 *res).
You don't really need to return the acpi_status, you just want to
return -1, 0, 1 for wmi_ec_cmd.

> +static int wmi_ec_cmd(unsigned char cmd, unsigned char arg,
> + Â Â Â Â Â Â Â Â Â Â unsigned short param1, unsigned short param2,
> + Â Â Â Â Â Â Â Â Â Â u32 *res)
> +{
> + Â Â Â struct shuttle_cmd scmd = {
> + Â Â Â Â Â Â Â .cmd = cmd,
> + Â Â Â Â Â Â Â .arg = arg,
> + Â Â Â Â Â Â Â .param1 = param1,
> + Â Â Â Â Â Â Â .param2 = param2
> + Â Â Â };
> +
> + Â Â Â if (ACPI_FAILURE(wmi_setget_mtd(&scmd, &res)))
> + Â Â Â Â Â Â Â return -1;
> + Â Â Â return (res) ? 0 : 1;
> +}
> +
> +struct shuttle_rfkill {
> + Â Â Â char *name;
> + Â Â Â struct rfkill *rfk;
> + Â Â Â enum rfkill_type type;
> + Â Â Â unsigned short fn;
> + Â Â Â u32 mask;
> + Â Â Â u32 list_on[3];
> + Â Â Â u32 list_off[3];
> +};
> +
> +static struct shuttle_rfkill shuttle_rfk_list[] = {
> + Â Â Â { "shuttle_wlan", NULL, RFKILL_TYPE_WLAN,
> + Â Â Â Â 0x04, 0x80, { 0x08 }, { 0x09 } },
> + Â Â Â { "shuttle_bluetooth", NULL, RFKILL_TYPE_BLUETOOTH,
> + Â Â Â Â 0x0d, 0x20, { 0x0c, 0x29 }, { 0x0d, 0x2a } },
> + Â Â Â { "shuttle_3g", NULL, RFKILL_TYPE_WWAN,
> + Â Â Â Â 0x05, 0x40, { 0x10, 0x29 }, { 0x11, 0x2a } },
> +};

I would rather use simple if/else constructs instead of list_on/list_off,
it would probably be more easy to read.

> +static int rfkill_common_set_block(void *data, bool blocked)
> +{
> + Â Â Â u32 val;
> + Â Â Â bool sw;
> + Â Â Â struct shuttle_rfkill *srfk = data;
> +
> + Â Â Â if (wmi_ec_cmd(CMD_READEC, 0, 0, ECRAM_ER1, &val))
> + Â Â Â Â Â Â Â return -EIO;
> + Â Â Â sw = val & srfk->mask;
> +
> + Â Â Â if ((blocked && sw) || (!blocked && !sw))

Maybe
sw = !!(val &  srfk->mask);
if (blocked == sw)
?

> + Â Â Â Â Â Â Â wmi_ec_cmd(CMD_SCMD, 0, 0, srfk->fn, NULL);
> + Â Â Â else
> + Â Â Â Â Â Â Â return 0;
> +
> + Â Â Â if (wmi_ec_cmd(CMD_READEC, 0, 0, ECRAM_ER1, &val))
> + Â Â Â Â Â Â Â return -EIO;
> + Â Â Â return ((val & srfk->mask) != blocked) ? 0 : -EIO;
> +}
> +
> +static const struct rfkill_ops rfkill_common_ops = {
> + Â Â Â .set_block = rfkill_common_set_block,
> +};
> +
> +static int common_rfkill_init(struct shuttle_rfkill *srfk, struct device *dev,
> + Â Â Â Â Â Â Â Â Â Â Â Â Â Â u32 init_val)
> +{
> + Â Â Â int rc;
> +
> + Â Â Â srfk->rfk = rfkill_alloc(srfk->name, dev, srfk->type,
> + Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â&rfkill_common_ops, srfk);
> + Â Â Â if (!srfk->rfk)
> + Â Â Â Â Â Â Â return -ENOMEM;
> +
> + Â Â Â rfkill_init_sw_state(srfk->rfk, !(init_val & srfk->mask));
> +
> + Â Â Â rc = rfkill_register(srfk->rfk);
> + Â Â Â if (rc) {
> + Â Â Â Â Â Â Â rfkill_destroy(srfk->rfk);
> + Â Â Â Â Â Â Â srfk->rfk = NULL;
> + Â Â Â Â Â Â Â return rc;
> + Â Â Â }
> +
> + Â Â Â return 0;
> +}
> +
> +static int shuttle_rfkill_init(struct platform_device *pdev)
> +{
> + Â Â Â int rc, i;
> + Â Â Â u32 val;
> + Â Â Â struct shuttle_rfkill *srfk;
> +
> + Â Â Â if (wmi_ec_cmd(CMD_READEC, 0, 0, ECRAM_ER1, &val))
> + Â Â Â Â Â Â Â return -EIO;
> +
> + Â Â Â for (i = 0; i < ARRAY_SIZE(shuttle_rfk_list); i++) {
> + Â Â Â Â Â Â Â srfk = &shuttle_rfk_list[i];
> +
> + Â Â Â Â Â Â Â /* check that hardware is available (when missing we can't
> + Â Â Â Â Â Â Â Â* unblock it), to avoid having rfkill switch when not needed;
> + Â Â Â Â Â Â Â Â* after check, reset to initial setting */
> + Â Â Â Â Â Â Â if (rfkill_common_set_block(srfk, false))
> + Â Â Â Â Â Â Â Â Â Â Â continue;
> + Â Â Â Â Â Â Â if (!(val & srfk->mask))
> + Â Â Â Â Â Â Â Â Â Â Â rfkill_common_set_block(srfk, true);

There is no other solution  to guess if the hardware is present ? Because
I don't think it's a good idea to toggle every devices on load (may take time,
etc...)

> + Â Â Â Â Â Â Â rc = common_rfkill_init(srfk, &pdev->dev, val);
> + Â Â Â Â Â Â Â if (rc)
> + Â Â Â Â Â Â Â Â Â Â Â goto err_rfk_init;
> + Â Â Â }
> + Â Â Â return 0;
> +
> +err_rfk_init:
> + Â Â Â for (i--; i >= 0; i--) {
> + Â Â Â Â Â Â Â srfk = &shuttle_rfk_list[i];
> + Â Â Â Â Â Â Â if (srfk->rfk) {
> + Â Â Â Â Â Â Â Â Â Â Â rfkill_unregister(srfk->rfk);
> + Â Â Â Â Â Â Â Â Â Â Â rfkill_destroy(srfk->rfk);
> + Â Â Â Â Â Â Â Â Â Â Â srfk->rfk = NULL;
> + Â Â Â Â Â Â Â }
> + Â Â Â }

Just call shuttfle_rfkill_remove, the if(srfk->rfk) should
make it work without any issue.

> + Â Â Â return rc;
> +}
> +
> +static void shuttle_rfkill_remove(void)
> +{
> + Â Â Â int i;
> + Â Â Â struct shuttle_rfkill *srfk;
> +
> + Â Â Â for (i = 0; i < ARRAY_SIZE(shuttle_rfk_list); i++) {
> + Â Â Â Â Â Â Â srfk = &shuttle_rfk_list[i];
> + Â Â Â Â Â Â Â if (srfk->rfk) {
> + Â Â Â Â Â Â Â Â Â Â Â rfkill_unregister(srfk->rfk);
> + Â Â Â Â Â Â Â Â Â Â Â rfkill_destroy(srfk->rfk);
> + Â Â Â Â Â Â Â Â Â Â Â srfk->rfk = NULL;
> + Â Â Â Â Â Â Â }
> + Â Â Â }
> +}
> +
> +static bool set_rfkill_sw(u32 *list, u32 code, struct rfkill *rfk, bool blocked)
> +{
> + Â Â Â while (*list) {
> + Â Â Â Â Â Â Â if (*list == code) {
> + Â Â Â Â Â Â Â Â Â Â Â rfkill_set_sw_state(rfk, blocked);
> + Â Â Â Â Â Â Â Â Â Â Â return true;
> + Â Â Â Â Â Â Â }
> + Â Â Â Â Â Â Â list++;
> + Â Â Â }
> + Â Â Â return false;
> +}
> +
> +static bool notify_switch_rfkill(u32 code)
> +{
> + Â Â Â int i;
> + Â Â Â struct rfkill *rfk;
> + Â Â Â u32 *rfk_evnt;
> + Â Â Â bool res = false;
> +
> + Â Â Â for (i = 0; i < ARRAY_SIZE(shuttle_rfk_list); i++) {
> + Â Â Â Â Â Â Â rfk = shuttle_rfk_list[i].rfk;
> + Â Â Â Â Â Â Â if (!rfk)
> + Â Â Â Â Â Â Â Â Â Â Â continue;
> +
> + Â Â Â Â Â Â Â rfk_evnt = shuttle_rfk_list[i].list_on;
> + Â Â Â Â Â Â Â if (set_rfkill_sw(rfk_evnt, code, rfk, false)) {
> + Â Â Â Â Â Â Â Â Â Â Â res = true;
> + Â Â Â Â Â Â Â Â Â Â Â continue;
> + Â Â Â Â Â Â Â }
> +
> + Â Â Â Â Â Â Â rfk_evnt = shuttle_rfk_list[i].list_off;
> + Â Â Â Â Â Â Â if (set_rfkill_sw(rfk_evnt, code, rfk, true))
> + Â Â Â Â Â Â Â Â Â Â Â res = true;
> + Â Â Â }
> + Â Â Â return res;
> +}

See my previous comment, but I believe that the code would be
more obvious with basic if/else (or at least a command explaining
what is does).

> +static bool notify_switch_attr(struct platform_device *pdev, u32 code)
> +{
> + Â Â Â int i;
> + Â Â Â struct shuttle_switch {
> + Â Â Â Â Â Â Â u32 switch_on;
> + Â Â Â Â Â Â Â u32 switch_off;
> + Â Â Â Â Â Â Â char *sys_attr;
> + Â Â Â };
> + Â Â Â static const struct shuttle_switch codes[] = {
> + Â Â Â Â Â Â Â { 0x04, 0x05, "touchpad" },
> + Â Â Â Â Â Â Â { 0x12, 0x13, "webcam" },
> + Â Â Â Â Â Â Â { 0x31, 0x32, "powersave" }
> + Â Â Â };
> +
> + Â Â Â for (i = 0; i < ARRAY_SIZE(codes); i++) {
> + Â Â Â Â Â Â Â if (codes[i].switch_on == code || codes[i].switch_off == code) {
> + Â Â Â Â Â Â Â Â Â Â Â sysfs_notify(&pdev->dev.kobj, NULL, codes[i].sys_attr);
> + Â Â Â Â Â Â Â Â Â Â Â return true;
> + Â Â Â Â Â Â Â }
> + Â Â Â }
> + Â Â Â return false;
> +}
> +
> +static void shuttle_wmi_notify(u32 value, void *data)
> +{
> + Â Â Â acpi_status status;
> + Â Â Â union acpi_object *obj;
> + Â Â Â u8 type;
> + Â Â Â u32 code;
> + Â Â Â struct acpi_buffer res = { ACPI_ALLOCATE_BUFFER, NULL };
> + Â Â Â struct shuttle_wmi_priv *priv = data;
> +
> + Â Â Â status = wmi_get_event_data(value, &res);
> + Â Â Â if (status != AE_OK) {
> + Â Â Â Â Â Â Â pr_warning("unable to retrieve wmi event status"
> + Â Â Â Â Â Â Â Â Â Â Â Â Â" (error=0x%x)\n", status);
> + Â Â Â Â Â Â Â return;
> + Â Â Â }
> +
> + Â Â Â obj = (union acpi_object *) res.pointer;
> + Â Â Â if (!obj)
> + Â Â Â Â Â Â Â return;
> + Â Â Â if (obj->type != ACPI_TYPE_INTEGER) {
> + Â Â Â Â Â Â Â pr_info("unknown object returned in wmi event\n");
> + Â Â Â Â Â Â Â goto notify_exit;
> + Â Â Â }
> +
> + Â Â Â type = (obj->integer.value >> 24) & 0xFF;
> + Â Â Â switch (type) {
> + Â Â Â case 0: /* OSD/Scancode event */
> + Â Â Â Â Â Â Â code = obj->integer.value & 0xFFFFFF;
> +
> + Â Â Â Â Â Â Â /* update rfkill switches */
> + Â Â Â Â Â Â Â if (notify_switch_rfkill(code))
> + Â Â Â Â Â Â Â Â Â Â Â break;
> +
> + Â Â Â Â Â Â Â /* send notification on state switch attributes */
> + Â Â Â Â Â Â Â if (notify_switch_attr(priv->pdev, code))
> + Â Â Â Â Â Â Â Â Â Â Â break;
> +
> + Â Â Â Â Â Â Â if (priv->bd && (code == 0x14 || code == 0x15))
> + Â Â Â Â Â Â Â Â Â Â Â backlight_force_update(priv->bd,
> + Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â ÂBACKLIGHT_UPDATE_HOTKEY);
> +
> + Â Â Â Â Â Â Â if (!sparse_keymap_report_event(priv->inputdev, code, 1, true))
> + Â Â Â Â Â Â Â Â Â Â Â pr_info("unhandled scancode (0x%06x)\n", code);
> + Â Â Â Â Â Â Â break;
> + Â Â Â case 1: /* Power management event */
> + Â Â Â Â Â Â Â /* Events not used.
> + Â Â Â Â Â Â Â Â* Possible values for obj->integer.value:
> + Â Â Â Â Â Â Â Â* 0x01000000 - silent mode
> + Â Â Â Â Â Â Â Â* 0x01010000 - brightness sync */
> + Â Â Â case 2: /* i-PowerXross event */
> + Â Â Â Â Â Â Â /* i-PowerXross is a overclocking feature, not
> + Â Â Â Â Â Â Â Â* implemented, there are no further details, possible
> + Â Â Â Â Â Â Â Â* values for obj->integer.value in documentation:
> + Â Â Â Â Â Â Â Â* 0x02000000 - idle mode
> + Â Â Â Â Â Â Â Â* 0x02010000 - action mode
> + Â Â Â Â Â Â Â Â* 0x02020000 - entry s3 */
> + Â Â Â Â Â Â Â break;
> + Â Â Â case 0xec: /* Lost event */
> + Â Â Â Â Â Â Â if (printk_ratelimit())
> + Â Â Â Â Â Â Â Â Â Â Â pr_warning("lost event because of buggy BIOS");
> + Â Â Â Â Â Â Â break;
> + Â Â Â default:
> + Â Â Â Â Â Â Â pr_info("unknown wmi notification type (0x%02x)\n", type);
> + Â Â Â }
> +
> +notify_exit:
> + Â Â Â kfree(obj);
> +}
> +
> +static const struct key_entry shuttle_wmi_keymap[] = {
> + Â Â Â { KE_KEY, 0x14, { KEY_BRIGHTNESSUP } },
> + Â Â Â { KE_KEY, 0x15, { KEY_BRIGHTNESSDOWN } },
> + Â Â Â { KE_KEY, 0x16, { KEY_FASTFORWARD } },
> + Â Â Â { KE_KEY, 0x17, { KEY_REWIND } },
> + Â Â Â { KE_KEY, 0x18, { KEY_F13 } }, /* OSD Beep */
> + Â Â Â { KE_KEY, 0x2b, { KEY_F14 } }, /* OSD menu 1 */
> + Â Â Â { KE_KEY, 0x2c, { KEY_F15 } }, /* OSD menu 2 */
> + Â Â Â { KE_KEY, 0x2d, { KEY_F16 } }, /* OSD menu 3 */
> + Â Â Â { KE_KEY, 0x2e, { KEY_F17 } }, /* OSD menu 4 */
> + Â Â Â { KE_KEY, 0x33, { KEY_F18 } }, /* Update OSD bar status */
> + Â Â Â { KE_KEY, 0x90, { KEY_WWW } },
> + Â Â Â { KE_KEY, 0x95, { KEY_PREVIOUSSONG } },
> + Â Â Â { KE_KEY, 0xa0, { KEY_PROG1 } }, /* Call OSD software */
> + Â Â Â { KE_KEY, 0xa1, { KEY_VOLUMEDOWN } },
> + Â Â Â { KE_KEY, 0xa3, { KEY_MUTE } },
> + Â Â Â { KE_KEY, 0xb2, { KEY_VOLUMEUP } },
> + Â Â Â { KE_KEY, 0xb4, { KEY_PLAYPAUSE } },
> + Â Â Â { KE_KEY, 0xbb, { KEY_STOPCD } },
> + Â Â Â { KE_KEY, 0xc8, { KEY_MAIL } },
> + Â Â Â { KE_KEY, 0xcd, { KEY_NEXTSONG } },
> + Â Â Â { KE_KEY, 0xd0, { KEY_MEDIA } },
> +
> + Â Â Â /* Known non hotkey events don't handled or that we don't care */
> + Â Â Â { KE_IGNORE, 0x01, }, /* Caps Lock toggled */
> + Â Â Â { KE_IGNORE, 0x02, }, /* Num Lock toggled */
> + Â Â Â { KE_IGNORE, 0x03, }, /* Scroll Lock toggled */
> + Â Â Â { KE_IGNORE, 0x06, }, /* Downclock/Silent on */
> + Â Â Â { KE_IGNORE, 0x07, }, /* Downclock/Silent off */
> + Â Â Â { KE_IGNORE, 0x0a, }, /* WiMax on */
> + Â Â Â { KE_IGNORE, 0x0b, }, /* WiMax off */
> + Â Â Â { KE_IGNORE, 0x0e, }, /* RF on */
> + Â Â Â { KE_IGNORE, 0x0f, }, /* RF off */
> + Â Â Â { KE_IGNORE, 0x1a, }, /* Auto Brightness on */
> + Â Â Â { KE_IGNORE, 0x1b, }, /* Auto Brightness off */
> + Â Â Â { KE_IGNORE, 0x1c, }, /* Auto-KB Brightness on */
> + Â Â Â { KE_IGNORE, 0x1d, }, /* Auto-KB Brightness off */
> + Â Â Â { KE_IGNORE, 0x1e, }, /* Light Bar Brightness up */
> + Â Â Â { KE_IGNORE, 0x1f, }, /* Light Bar Brightness down */
> + Â Â Â { KE_IGNORE, 0x20, }, /* China Telecom AP enable */
> + Â Â Â { KE_IGNORE, 0x21, }, /* China Mobile AP enable */
> + Â Â Â { KE_IGNORE, 0x22, }, /* Huawei AP enable */
> + Â Â Â { KE_IGNORE, 0x23, }, /* Docking in */
> + Â Â Â { KE_IGNORE, 0x24, }, /* Docking out */
> + Â Â Â { KE_IGNORE, 0x25, }, /* Device no function */
> + Â Â Â { KE_IGNORE, 0x26, }, /* i-PowerXross OverClocking */
> + Â Â Â { KE_IGNORE, 0x27, }, /* i-PowerXross PowerSaving */
> + Â Â Â { KE_IGNORE, 0x28, }, /* i-PowerXross off */
> + Â Â Â { KE_IGNORE, 0x2f, }, /* Optimus on */
> + Â Â Â { KE_IGNORE, 0x30, }, /* Optimus off */
> + Â Â Â { KE_IGNORE, 0x91, }, /* ICO 2 on */
> + Â Â Â { KE_IGNORE, 0x92, }, /* ICO 2 off */
> +
> + Â Â Â { KE_END, 0 }
> +};
> +
> +static int shuttle_wmi_input_init(struct shuttle_wmi_priv *priv)
> +{
> + Â Â Â struct input_dev *input;
> + Â Â Â int rc;
> +
> + Â Â Â input = input_allocate_device();
> + Â Â Â if (!input)
> + Â Â Â Â Â Â Â return -ENOMEM;
> +
> + Â Â Â input->name = "Shuttle WMI hotkeys";
> + Â Â Â input->phys = KBUILD_MODNAME "/input0";
> + Â Â Â input->id.bustype = BUS_HOST;
> +
> + Â Â Â rc = sparse_keymap_setup(input, shuttle_wmi_keymap, NULL);
> + Â Â Â if (rc)
> + Â Â Â Â Â Â Â goto err_free_dev;
> +
> + Â Â Â rc = input_register_device(input);
> + Â Â Â if (rc)
> + Â Â Â Â Â Â Â goto err_free_keymap;
> +
> + Â Â Â priv->inputdev = input;
> + Â Â Â return 0;
> +
> +err_free_keymap:
> + Â Â Â sparse_keymap_free(input);
> +err_free_dev:
> + Â Â Â input_free_device(input);
> + Â Â Â return rc;
> +}
> +
> +static void shuttle_wmi_input_remove(struct shuttle_wmi_priv *priv)
> +{
> + Â Â Â struct input_dev *input = priv->inputdev;
> +
> + Â Â Â sparse_keymap_free(input);
> + Â Â Â input_unregister_device(input);
> +}
> +
> +static int shuttle_wmi_get_bl(struct backlight_device *bd)
> +{
> + Â Â Â u32 val;
> +
> + Â Â Â if (wmi_ec_cmd(CMD_READEC, 0, 0, ECRAM_ER0, &val))
> + Â Â Â Â Â Â Â return -EIO;
> + Â Â Â return val & 0x7;
> +}
> +
> +static int shuttle_wmi_update_bl(struct backlight_device *bd)
> +{
> + Â Â Â int rc, steps;
> + Â Â Â u32 val;
> + Â Â Â struct shuttle_wmi_priv *priv = bl_get_data(bd);
> +
> + Â Â Â if (!priv->id->step_brightness) {
> + Â Â Â Â Â Â Â rc = ec_write(0x79, bd->props.brightness);
> + Â Â Â Â Â Â Â if (rc)
> + Â Â Â Â Â Â Â Â Â Â Â return rc;
> + Â Â Â } else {
> + Â Â Â Â Â Â Â /* change brightness by steps, this is a quirk for shuttle
> + Â Â Â Â Â Â Â Â* machines which don't accept direct write to ec for this */
> + Â Â Â Â Â Â Â if (wmi_ec_cmd(CMD_READEC, 0, 0, ECRAM_ER0, &val))
> + Â Â Â Â Â Â Â Â Â Â Â return -EIO;
> + Â Â Â Â Â Â Â val &= 0x7;
> + Â Â Â Â Â Â Â steps = bd->props.brightness - val;
> + Â Â Â Â Â Â Â while (steps > 0) {
> + Â Â Â Â Â Â Â Â Â Â Â wmi_ec_cmd(CMD_SCMD, 0, 0, 0x0c, NULL);
> + Â Â Â Â Â Â Â Â Â Â Â steps--;
> + Â Â Â Â Â Â Â }
> + Â Â Â Â Â Â Â while (steps < 0) {
> + Â Â Â Â Â Â Â Â Â Â Â wmi_ec_cmd(CMD_SCMD, 0, 0, 0x0b, NULL);
> + Â Â Â Â Â Â Â Â Â Â Â steps++;
> + Â Â Â Â Â Â Â }
> + Â Â Â }
> + Â Â Â return 0;
> +}
> +
> +static const struct backlight_ops shuttle_wmi_bl_ops = {
> + Â Â Â .get_brightness = shuttle_wmi_get_bl,
> + Â Â Â .update_status = shuttle_wmi_update_bl,
> +};
> +
> +static int shuttle_wmi_backlight_init(struct shuttle_wmi_priv *priv)
> +{
> + Â Â Â u32 val;
> + Â Â Â struct backlight_properties props;
> + Â Â Â struct backlight_device *bd;
> +
> + Â Â Â if (wmi_ec_cmd(CMD_READEC, 0, 0, ECRAM_ER0, &val))
> + Â Â Â Â Â Â Â return -EIO;
> + Â Â Â val &= 0x7;
> + Â Â Â memset(&props, 0, sizeof(struct backlight_properties));
> + Â Â Â props.max_brightness = 7;
> + Â Â Â props.brightness = val;
> + Â Â Â props.power = FB_BLANK_UNBLANK;
> +
> + Â Â Â bd = backlight_device_register(KBUILD_MODNAME, &priv->pdev->dev, priv,
> + Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â&shuttle_wmi_bl_ops, &props);
> + Â Â Â if (IS_ERR(bd))
> + Â Â Â Â Â Â Â return PTR_ERR(bd);
> + Â Â Â priv->bd = bd;
> + Â Â Â return 0;
> +}
> +
> +static void shuttle_wmi_backlight_exit(struct shuttle_wmi_priv *priv)
> +{
> + Â Â Â if (priv->bd)
> + Â Â Â Â Â Â Â backlight_device_unregister(priv->bd);
> +}
> +
> +static int __devinit shuttle_wmi_probe(struct platform_device *pdev)
> +{
> + Â Â Â struct shuttle_wmi_priv *priv;
> + Â Â Â int rc, i;
> + Â Â Â acpi_status status;
> + Â Â Â u32 val;
> + Â Â Â static struct shuttle_id id_unknown = {
> + Â Â Â Â Â Â Â .model_name = "Unknown",
> + Â Â Â };
> +
> + Â Â Â priv = kzalloc(sizeof(struct shuttle_wmi_priv), GFP_KERNEL);
> + Â Â Â if (!priv)
> + Â Â Â Â Â Â Â return -ENOMEM;
> + Â Â Â priv->pdev = pdev;
> + Â Â Â platform_set_drvdata(pdev, priv);
> +
> + Â Â Â rc = shuttle_rfkill_init(pdev);
> + Â Â Â if (rc)
> + Â Â Â Â Â Â Â goto err_rfk;
> +
> + Â Â Â rc = shuttle_wmi_input_init(priv);
> + Â Â Â if (rc)
> + Â Â Â Â Â Â Â goto err_input;
> +
> + Â Â Â status = wmi_install_notify_handler(SHUTTLE_WMI_EVENT_GUID,
> + Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â shuttle_wmi_notify, priv);
> + Â Â Â if (ACPI_FAILURE(status)) {
> + Â Â Â Â Â Â Â rc = -EIO;
> + Â Â Â Â Â Â Â goto err_notify;
> + Â Â Â }
> +
> + Â Â Â for (i = 0; i < ARRAY_SIZE(shuttle_ids); i++) {
> + Â Â Â Â Â Â Â rc = wmi_ec_cmd(shuttle_ids[i].cmd_id, 0, 0, 0, &val);
> + Â Â Â Â Â Â Â if (!rc && val == 1) {
> + Â Â Â Â Â Â Â Â Â Â Â priv->id = &shuttle_ids[i];
> + Â Â Â Â Â Â Â Â Â Â Â break;
> + Â Â Â Â Â Â Â }
> + Â Â Â }
> + Â Â Â if (i == ARRAY_SIZE(shuttle_ids))
> + Â Â Â Â Â Â Â priv->id = &id_unknown;
> +
> + Â Â Â if (!acpi_video_backlight_support()) {
> + Â Â Â Â Â Â Â rc = shuttle_wmi_backlight_init(priv);
> + Â Â Â Â Â Â Â if (rc)
> + Â Â Â Â Â Â Â Â Â Â Â goto err_backlight;
> + Â Â Â }
> + Â Â Â return 0;
> +
> +err_backlight:
> + Â Â Â wmi_remove_notify_handler(SHUTTLE_WMI_EVENT_GUID);
> +err_notify:
> + Â Â Â shuttle_wmi_input_remove(priv);
> +err_input:
> + Â Â Â shuttle_rfkill_remove();
> +err_rfk:
> + Â Â Â kfree(priv);
> + Â Â Â return rc;
> +}
> +
> +static int __devexit shuttle_wmi_remove(struct platform_device *pdev)
> +{
> + Â Â Â struct shuttle_wmi_priv *priv = platform_get_drvdata(pdev);
> +
> + Â Â Â shuttle_wmi_backlight_exit(priv);
> + Â Â Â wmi_remove_notify_handler(SHUTTLE_WMI_EVENT_GUID);
> + Â Â Â shuttle_wmi_input_remove(priv);
> + Â Â Â shuttle_rfkill_remove();
> + Â Â Â kfree(priv);
> + Â Â Â return 0;
> +}
> +
> +#ifdef CONFIG_PM

WMI depends on ACPI
ACPI depends on PM

So it's not really needed.

> +static int shuttle_wmi_resume(struct device *dev)
> +{
> + Â Â Â u32 val;
> + Â Â Â int i;
> + Â Â Â struct shuttle_rfkill *srfk;
> +
> + Â Â Â if (wmi_ec_cmd(CMD_READEC, 0, 0, ECRAM_ER1, &val))
> + Â Â Â Â Â Â Â return -EIO;
> +
> + Â Â Â for (i = 0; i < ARRAY_SIZE(shuttle_rfk_list); i++) {
> + Â Â Â Â Â Â Â srfk = &shuttle_rfk_list[i];
> + Â Â Â Â Â Â Â if (srfk->rfk)
> + Â Â Â Â Â Â Â Â Â Â Â rfkill_set_sw_state(srfk->rfk, !(val & srfk->mask));
> + Â Â Â }
> + Â Â Â return 0;
> +}

You're code would probably be cleaner with a rfkill helper to get the current
state of a given deivce, to avoid playing with masks everytime.

> +
> +static const struct dev_pm_ops shuttle_wmi_pm_ops = {
> + Â Â Â .restore = shuttle_wmi_resume,
> + Â Â Â .resume = shuttle_wmi_resume,
> +};
> +#endif
> +
> +static struct platform_driver shuttle_wmi_driver = {
> + Â Â Â .driver = {
> + Â Â Â Â Â Â Â .name = KBUILD_MODNAME,
> + Â Â Â Â Â Â Â .owner = THIS_MODULE,
> +#ifdef CONFIG_PM
> + Â Â Â Â Â Â Â .pm = &shuttle_wmi_pm_ops,
> +#endif
> + Â Â Â },
> + Â Â Â .probe = shuttle_wmi_probe,
> + Â Â Â .remove = __devexit_p(shuttle_wmi_remove),
> +};
> +
> +static struct platform_device *shuttle_wmi_device;
> +
> +static ssize_t show_state_common(char *buf, unsigned short ecram, u32 mask)
> +{
> + Â Â Â u32 val;
> +
> + Â Â Â if (wmi_ec_cmd(CMD_READEC, 0, 0, ecram, &val))
> + Â Â Â Â Â Â Â return -EIO;
> + Â Â Â return sprintf(buf, "%d\n", (val & mask) ? 1 : 0);
> +}
> +
> +static ssize_t store_state_common(const char *buf, size_t count,
> + Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â unsigned short ecram, u32 mask,
> + Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â unsigned short fn)
> +{
> + Â Â Â int enable;
> + Â Â Â int rc;
> + Â Â Â u32 val;
> +
> + Â Â Â if (sscanf(buf, "%i", &enable) != 1)
> + Â Â Â Â Â Â Â return -EINVAL;
> +
> + Â Â Â if (wmi_ec_cmd(CMD_READEC, 0, 0, ecram, &val))
> + Â Â Â Â Â Â Â return -EIO;
> + Â Â Â val &= mask;

Here again, having to use a mask makes the code harder to read.
Can't you use !!(val & mask) instead of val & mask ? Maybe a wrapper
around CMD_READEC to do that ? int wmi_ec_read_state(ecram, mask) ?

> + Â Â Â if ((val && !enable) ||
> + Â Â Â Â Â (!val && enable)) {
> + Â Â Â Â Â Â Â wmi_ec_cmd(CMD_SCMD, 0, 0, fn, NULL);
> + Â Â Â Â Â Â Â rc = wmi_ec_cmd(CMD_READEC, 0, 0, ecram, &val);
> + Â Â Â Â Â Â Â val &= mask;
> + Â Â Â Â Â Â Â if (rc || (!val && enable) ||
> + Â Â Â Â Â Â Â Â Â (val && !enable))
> + Â Â Â Â Â Â Â Â Â Â Â return -EIO;
> + Â Â Â }
> + Â Â Â return count;
> +}
> +
> +#define SHUTTLE_STATE_ATTR(_name, _ecram, _mask, _fn) Â Â Â Â Â Â Â Â Â\
> + Â Â Â static ssize_t show_##_name(struct device *dev, Â Â Â Â Â Â Â Â \
> + Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â struct device_attribute *attr, Â Â Â\
> + Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â char *buf) Â Â Â Â Â Â Â Â Â Â Â Â Â\
> + Â Â Â { Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â \
> + Â Â Â Â Â Â Â return show_state_common(buf, _ecram, _mask); Â Â Â Â Â \
> + Â Â Â } Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â \
> + Â Â Â static ssize_t store_##_name(struct device *dev, Â Â Â Â Â Â Â Â\
> + Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Âstruct device_attribute *attr, Â Â \
> + Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Âconst char *buf, size_t count) Â Â \
> + Â Â Â { Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â \
> + Â Â Â Â Â Â Â return store_state_common(buf, count, _ecram, _mask, Â Â\
> + Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â _fn); Â Â Â Â Â Â Â Â Â Â Â Â \
> + Â Â Â } Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â \
> + Â Â Â static DEVICE_ATTR(_name, 0644, show_##_name, store_##_name);
> +
> +SHUTTLE_STATE_ATTR(powersave, ECRAM_ER2, 0x10, 0x02)
> +SHUTTLE_STATE_ATTR(touchpad, ECRAM_ER1, 0x02, 0x06)
> +SHUTTLE_STATE_ATTR(webcam, ECRAM_ER1, 0x10, 0x07)
> +
> +#define SHUTTLE_CMD_ATTR(_name, _cmd, _arg, _fn) Â Â Â Â Â Â Â Â Â Â Â \
> + Â Â Â static ssize_t store_##_name(struct device *dev, Â Â Â Â Â Â Â Â\
> + Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Âstruct device_attribute *attr, Â Â \
> + Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Âconst char *buf, size_t count) Â Â \
> + Â Â Â { Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â \
> + Â Â Â Â Â Â Â wmi_ec_cmd(_cmd, _arg, 0, _fn, NULL); Â Â Â Â Â Â Â Â Â \
> + Â Â Â Â Â Â Â return count; Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â \
> + Â Â Â } Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â \
> + Â Â Â static DEVICE_ATTR(_name, 0200, NULL, store_##_name);
> +
> +SHUTTLE_CMD_ATTR(sleep, CMD_SCMD, 0, 0x01)
> +SHUTTLE_CMD_ATTR(switch_video, CMD_SCMD, 0, 0x03)
> +SHUTTLE_CMD_ATTR(sound_mute, CMD_SCMD, 0, 0x08)
> +SHUTTLE_CMD_ATTR(volume_down, CMD_SCMD, 0, 0x09)
> +SHUTTLE_CMD_ATTR(volume_up, CMD_SCMD, 0, 0x0a)
> +SHUTTLE_CMD_ATTR(brightness_down, CMD_SCMD, 0, 0x0b)
> +SHUTTLE_CMD_ATTR(brightness_up, CMD_SCMD, 0, 0x0c)
> +SHUTTLE_CMD_ATTR(lcd_auto_adjust, CMD_SCMD, 0, 0x81)
> +SHUTTLE_CMD_ATTR(white_balance, CMD_SCMD, 0, 0x82)
> +SHUTTLE_CMD_ATTR(panel_set_default, CMD_SCMD, 0, 0x83)
> +SHUTTLE_CMD_ATTR(lbar_brightness_down, CMD_LCTRL, 1, 0)
> +SHUTTLE_CMD_ATTR(lbar_brightness_up, CMD_LCTRL, 1, 1)
> +
> +static ssize_t show_model_name(struct device *dev,
> + Â Â Â Â Â Â Â Â Â Â Â Â Â Â Âstruct device_attribute *attr,
> + Â Â Â Â Â Â Â Â Â Â Â Â Â Â Âchar *buf)
> +{
> + Â Â Â struct platform_device *pdev = to_platform_device(dev);
> + Â Â Â struct shuttle_wmi_priv *priv = platform_get_drvdata(pdev);
> +
> + Â Â Â return sprintf(buf, "%s\n", priv->id->model_name);
> +}
> +
> +static DEVICE_ATTR(model_name, 0444, show_model_name, NULL);
> +
> +static ssize_t store_cut_lvds(struct device *dev,
> + Â Â Â Â Â Â Â Â Â Â Â Â Â Â struct device_attribute *attr,
> + Â Â Â Â Â Â Â Â Â Â Â Â Â Â const char *buf, size_t count)
> +{
> + Â Â Â int cut;
> +
> + Â Â Â if (sscanf(buf, "%i", &cut) != 1)
> + Â Â Â Â Â Â Â return -EINVAL;
> +
> + Â Â Â if (cut)
> + Â Â Â Â Â Â Â wmi_ec_cmd(CMD_CUTLVDS, 0, 0, 1, NULL);
> + Â Â Â else
> + Â Â Â Â Â Â Â wmi_ec_cmd(CMD_CUTLVDS, 0, 0, 0, NULL);
> +
> + Â Â Â return count;
> +}
> +static DEVICE_ATTR(cut_lvds, 0200, NULL, store_cut_lvds);
> +
> +static struct attribute *shuttle_platform_attributes[] = {
> + Â Â Â &dev_attr_powersave.attr,
> + Â Â Â &dev_attr_touchpad.attr,
> + Â Â Â &dev_attr_webcam.attr,
> + Â Â Â &dev_attr_sleep.attr,
> + Â Â Â &dev_attr_switch_video.attr,
> + Â Â Â &dev_attr_sound_mute.attr,
> + Â Â Â &dev_attr_volume_down.attr,
> + Â Â Â &dev_attr_volume_up.attr,
> + Â Â Â &dev_attr_brightness_down.attr,
> + Â Â Â &dev_attr_brightness_up.attr,
> + Â Â Â &dev_attr_lcd_auto_adjust.attr,
> + Â Â Â &dev_attr_white_balance.attr,
> + Â Â Â &dev_attr_panel_set_default.attr,
> + Â Â Â &dev_attr_lbar_brightness_down.attr,
> + Â Â Â &dev_attr_lbar_brightness_up.attr,
> + Â Â Â &dev_attr_model_name.attr,
> + Â Â Â &dev_attr_cut_lvds.attr,
> + Â Â Â NULL
> +};
> +
> +static struct attribute_group shuttle_attribute_group = {
> + Â Â Â .attrs = shuttle_platform_attributes
> +};
> +
> +static int __init shuttle_wmi_init(void)
> +{
> + Â Â Â int rc;
> + Â Â Â u32 val;
> +
> + Â Â Â if (!wmi_has_guid(SHUTTLE_WMI_SETGET_GUID) ||
> + Â Â Â Â Â !wmi_has_guid(SHUTTLE_WMI_EVENT_GUID)) {
> + Â Â Â Â Â Â Â pr_err("Required WMI GUID not available\n");
> + Â Â Â Â Â Â Â return -ENODEV;
> + Â Â Â }
> +
> + Â Â Â /* Check that we are really on a shuttle BIOS */
> + Â Â Â rc = wmi_ec_cmd(CMD_INT15, 0, 0, 0, &val);
> + Â Â Â if (rc || val != 0x534c) {
> + Â Â Â Â Â Â Â pr_err("Shuttle WMI device not found or unsupported"
> + Â Â Â Â Â Â Â Â Â Â Â" (val=0x%08x)\n", val);
> + Â Â Â Â Â Â Â return -ENODEV;
> + Â Â Â }
> +
> + Â Â Â rc = platform_driver_register(&shuttle_wmi_driver);
> + Â Â Â if (rc)
> + Â Â Â Â Â Â Â goto err_driver_register;
> + Â Â Â shuttle_wmi_device = platform_device_alloc(KBUILD_MODNAME, -1);
> + Â Â Â if (!shuttle_wmi_device) {
> + Â Â Â Â Â Â Â rc = -ENOMEM;
> + Â Â Â Â Â Â Â goto err_device_alloc;
> + Â Â Â }
> + Â Â Â rc = platform_device_add(shuttle_wmi_device);
> + Â Â Â if (rc)
> + Â Â Â Â Â Â Â goto err_device_add;
> +
> + Â Â Â rc = sysfs_create_group(&shuttle_wmi_device->dev.kobj,
> + Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â &shuttle_attribute_group);
> + Â Â Â if (rc)
> + Â Â Â Â Â Â Â goto err_sysfs;
> +
> + Â Â Â return 0;
> +
> +err_sysfs:
> + Â Â Â platform_device_del(shuttle_wmi_device);
> +err_device_add:
> + Â Â Â platform_device_put(shuttle_wmi_device);
> +err_device_alloc:
> + Â Â Â platform_driver_unregister(&shuttle_wmi_driver);
> +err_driver_register:
> + Â Â Â return rc;
> +}
> +
> +static void __exit shuttle_wmi_exit(void)
> +{
> + Â Â Â sysfs_remove_group(&shuttle_wmi_device->dev.kobj,
> + Â Â Â Â Â Â Â Â Â Â Â Â Â&shuttle_attribute_group);
> + Â Â Â platform_device_unregister(shuttle_wmi_device);
> + Â Â Â platform_driver_unregister(&shuttle_wmi_driver);
> +}
> +
> +module_init(shuttle_wmi_init);
> +module_exit(shuttle_wmi_exit);
> --
> 1.7.3.2
>
> --
> []'s
> Herton
> --
> To unsubscribe from this list: send the line "unsubscribe platform-driver-x86" in
> the body of a message to majordomo@xxxxxxxxxxxxxxx
> More majordomo info at Âhttp://vger.kernel.org/majordomo-info.html
>



-- 
Corentin Chary
http://xf.iksaif.net
--
To unsubscribe from this list: send the line "unsubscribe platform-driver-x86" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html


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

  Powered by Linux