Re: [PATCH V2] platform-driver-x86: ACPI EC Extra driver for Oaktrail

From: Corentin Chary
Date: Fri Jan 07 2011 - 03:56:19 EST


On Fri, Jan 7, 2011 at 8:41 AM, Yin Kangkai <kangkai.yin@xxxxxxxxxxxxxxx> wrote:
> Patch V2
> Changes from V1:
> Â- remove wifi, bt, gps, wwan from /sys/devices/platform.
> Â- add some helper function for rfkill alloc and rfkill cleanup.
> Â- add MODULE_ALIAS.
> Â- add Documentation/ABI/testing/sysfs-platform-intel-oaktrail
> Â- various other minor changes.
> Â- bump driver version from 0.1 to 0.2
>
> Thanks Corentin Chary and Joey Lee for the review.
>
> From ab28d41e89b40e0ab1d73489b7d1a9f6e745c14a Mon Sep 17 00:00:00 2001
> From: Yin Kangkai <kangkai.yin@xxxxxxxxx>
> Date: Wed, 22 Dec 2010 10:53:36 +0800
> Subject: [PATCH] platform-driver-x86: ACPI EC Extra driver for Oaktrail
>
> This driver implements an Extra ACPI EC driver for products based on Intel
> Oaktrail platform. ÂIt is programming the EC space, through existing ACPI EC
> driver, to provide user space layer the sysfs and rfkill interfaces to
> enable/disable the Camera, Bluetooth, GPS, WiFi, 3G, and to show the status of
> Touchscreen.
>
> Signed-off-by: Yin Kangkai <kangkai.yin@xxxxxxxxx>
> ---
> Â.../ABI/testing/sysfs-platform-intel-oaktrail   Â|  13 +
> Âdrivers/platform/x86/Kconfig            |  Â9 +
> Âdrivers/platform/x86/Makefile           Â|  Â1 +
> Âdrivers/platform/x86/intel_oaktrail.c       Â| Â347 ++++++++++++++++++++
> Â4 files changed, 370 insertions(+), 0 deletions(-)
> Âcreate mode 100644 Documentation/ABI/testing/sysfs-platform-intel-oaktrail
> Âcreate mode 100644 drivers/platform/x86/intel_oaktrail.c
>
> diff --git a/Documentation/ABI/testing/sysfs-platform-intel-oaktrail b/Documentation/ABI/testing/sysfs-platform-intel-oaktrail
> new file mode 100644
> index 0000000..0512235
> --- /dev/null
> +++ b/Documentation/ABI/testing/sysfs-platform-intel-oaktrail
> @@ -0,0 +1,13 @@
> +What: Â Â Â Â Â/sys/devices/platform/intel_oaktrail/camera
> +Date: Â Â Â Â ÂJan 2011
> +KernelVersion: 2.6.37
> +Contact: Â Â Â "Yin Kangkai" <kangkai.yin@xxxxxxxxx>
> +Description:
> + Â Â Â Â Â Â Â Control the camera. 1 means on, 0 means off.
> +
> +What: Â Â Â Â Â/sys/devices/platform/intel_oaktrail/touchscreen
> +Date: Â Â Â Â ÂJan 2011
> +KernelVersion: 2.6.37
> +Contact: Â Â Â "Yin Kangkai" <kangkai.yin@xxxxxxxxx>
> +Description:
> + Â Â Â Â Â Â Â Show the status of the touch screen. 1 means on, 0 means off.
> diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig
> index 2b4038a..d1f6981 100644
> --- a/drivers/platform/x86/Kconfig
> +++ b/drivers/platform/x86/Kconfig
> @@ -655,4 +655,13 @@ config XO1_RFKILL
> Â Â Â Â ÂSupport for enabling/disabling the WLAN interface on the OLPC XO-1
> Â Â Â Â Âlaptop.
>
> +config INTEL_OAKTRAIL
> + Â Â Â tristate "Intel Oaktrail Platform Extras"
> + Â Â Â depends on ACPI
> + Â Â Â depends on RFKILL
> + Â Â Â ---help---
> + Â Â Â Â Intel Oaktrail platform need this driver to provide interfaces to
> + Â Â Â Â enable/disable the Camera, WiFi, BT etc. devices. If in doubt, say Y
> + Â Â Â Â here; it will only load on supported platforms.
> +
> Âendif # X86_PLATFORM_DEVICES
> diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile
> index 7ff60e6..add8ab7 100644
> --- a/drivers/platform/x86/Makefile
> +++ b/drivers/platform/x86/Makefile
> @@ -35,3 +35,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_INTEL_OAKTRAIL) Â += intel_oaktrail.o
> diff --git a/drivers/platform/x86/intel_oaktrail.c b/drivers/platform/x86/intel_oaktrail.c
> new file mode 100644
> index 0000000..9ec71c5
> --- /dev/null
> +++ b/drivers/platform/x86/intel_oaktrail.c
> @@ -0,0 +1,347 @@
> +/*
> + ÂCopyright (C) 2010 Intel Corporation
> + ÂAuthor: Yin Kangkai (kangkai.yin@xxxxxxxxx)
> +
> + Âbased on Compal driver
> +
> + ÂCopyright (C) 2008 Cezary Jackiewicz <cezary.jackiewicz (at) gmail.com>
> +
> + Âbased on MSI driver
> +
> + ÂCopyright (C) 2006 Lennart Poettering <mzxreary (at) 0pointer (dot) de>
> +
> + Â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.
> +
> + Â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., 51 Franklin Street, Fifth Floor, Boston, MA
> + Â02110-1301, USA.
> + */
> +
> +/*
> + * intel_oaktrail.c - Intel OakTrail Platform support.
> + *
> + * This driver exports a few files in /sys/devices/platform/intel_oaktrail/:
> + *
> + * camera - Camera subsystem enabled: contains either 0 or 1. (rw)
> + * touchscreen - Touchscreen subsystem enabled: contains either 0 or 1. (ro)
> + *
> + * In addition to these platform device attributes, the driver registers itself
> + * in the Linux rfkill subsystem for these components:
> + *
> + * wifi
> + * bluetooth
> + * wwan (3g)
> + * gps
> + *
> + * and is available to userspace under /sys/class/rfkill/rfkillX/
> + *
> + * This driver might work on other products based on Oaktrail. If you
> + * want to try it you can pass force=1 as argument to the module which
> + * will force it to load even when the DMI data doesn't identify the
> + * product as compatible.
> + */
> +
> +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
> +
> +#include <linux/module.h>
> +#include <linux/kernel.h>
> +#include <linux/init.h>
> +#include <linux/acpi.h>
> +#include <linux/platform_device.h>
> +#include <linux/dmi.h>
> +#include <linux/rfkill.h>
> +
> +#define DRIVER_NAME Â Â"intel_oaktrail"
> +#define DRIVER_VERSION "0.2"
> +
> +/*
> + * This is the devices status address in EC space, and the control bits
> + * definition:
> + *
> + * (1 << 0): Â Camera enable/disable, RW.
> + * (1 << 1): Â Bluetooth enable/disable, RW.
> + * (1 << 2): Â GPS enable/disable, RW.
> + * (1 << 3): Â WiFi enable/disable, RW.
> + * (1 << 4): Â WWAN (3G) enable/disalbe, RW.
> + * (1 << 5): Â Touchscreen enable/disable, Read Only.
> + */
> +#define OT_EC_DEVICE_STATE_ADDRESS Â Â 0xD6
> +
> +#define OT_EC_CAMERA_MASK Â Â Â(1 << 0)
> +#define OT_EC_BT_MASK Â Â Â Â Â(1 << 1)
> +#define OT_EC_GPS_MASK Â Â Â Â (1 << 2)
> +#define OT_EC_WIFI_MASK Â Â Â Â Â Â Â Â(1 << 3)
> +#define OT_EC_WWAN_MASK Â Â Â Â Â Â Â Â(1 << 4)
> +#define OT_EC_TS_MASK Â Â Â Â Â(1 << 5)
> +
> +static int force;
> +module_param(force, bool, 0);
> +MODULE_PARM_DESC(force, "Force driver load, ignore DMI data");
> +
> +static struct platform_device *oaktrail_device;
> +static struct rfkill *bt_rfkill;
> +static struct rfkill *gps_rfkill;
> +static struct rfkill *wifi_rfkill;
> +static struct rfkill *wwan_rfkill;
> +
> +#define SIMPLE_MASKED_STORE_SHOW(NAME, MASK) Â Â Â Â Â Â Â Â Â Â Â Â Â \
> +static ssize_t NAME##_show(struct device *dev, Â Â Â Â Â Â Â Â Â Â Â Â \
> + Â Â Â struct device_attribute *attr, char *buf) Â Â Â Â Â Â Â Â Â Â Â \
> +{ Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â\
> + Â Â Â u8 value; Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â \
> + Â Â Â ec_read(OT_EC_DEVICE_STATE_ADDRESS, &value); Â Â Â Â Â Â Â Â Â Â\
> + Â Â Â return sprintf(buf, "%d\n", ((value & MASK) != 0)); Â Â Â Â Â Â \
> +} Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â\
> +static ssize_t NAME##_store(struct device *dev, Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â\
> + Â Â Â struct device_attribute *attr, const char *buf, size_t count) Â \
> +{ Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â\
> + Â Â Â int state; Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â\
> + Â Â Â u8 old_val; Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â \
> + Â Â Â ec_read(OT_EC_DEVICE_STATE_ADDRESS, &old_val); Â Â Â Â Â Â Â Â Â\
> + Â Â Â if (sscanf(buf, "%d", &state) != 1 || (state < 0 || state > 1)) \
> + Â Â Â Â Â Â Â return -EINVAL; Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â \
> + Â Â Â ec_write(OT_EC_DEVICE_STATE_ADDRESS, state ? Â Â Â Â Â Â Â Â Â Â\
> + Â Â Â Â Â Â Â Â(old_val | MASK) : (old_val & ~MASK)); Â Â Â Â Â Â Â Â \
> + Â Â Â return count; Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â \
> +}
> +
> +SIMPLE_MASKED_STORE_SHOW(camera, OT_EC_CAMERA_MASK)
> +SIMPLE_MASKED_STORE_SHOW(touchscreen, OT_EC_TS_MASK)
> +
> +static DEVICE_ATTR(camera, 0644, camera_show, camera_store);
> +static DEVICE_ATTR(touchscreen, 0444, touchscreen_show, NULL);
> +
> +static struct attribute *oaktrail_attributes[] = {
> + Â Â Â &dev_attr_camera.attr,
> + Â Â Â &dev_attr_touchscreen.attr,
> + Â Â Â NULL
> +};
> +
> +static struct attribute_group oaktrail_attribute_group = {
> + Â Â Â .attrs = oaktrail_attributes
> +};
> +
> +static int oaktrail_rfkill_set(void *data, bool blocked)
> +{
> + Â Â Â u8 value;
> + Â Â Â u8 result;
> + Â Â Â unsigned long radio = (unsigned long) data;
> +
> + Â Â Â ec_read(OT_EC_DEVICE_STATE_ADDRESS, &result);
> +
> + Â Â Â if (!blocked)
> + Â Â Â Â Â Â Â value = (u8) (result | radio);
> + Â Â Â else
> + Â Â Â Â Â Â Â value = (u8) (result & ~radio);
> +
> + Â Â Â ec_write(OT_EC_DEVICE_STATE_ADDRESS, value);
> +
> + Â Â Â return 0;
> +}
> +
> +static const struct rfkill_ops oaktrail_rfkill_ops = {
> + Â Â Â .set_block = oaktrail_rfkill_set,
> +};
> +
> +static struct rfkill *oaktrail_rfkill_new(char *name, enum rfkill_type type,
> + Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â unsigned long mask)
> +{
> + Â Â Â int err;
> + Â Â Â struct rfkill *rfkill_dev;
> +
> + Â Â Â rfkill_dev = rfkill_alloc(name, &oaktrail_device->dev, type,
> + Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â &oaktrail_rfkill_ops, (void *)mask);
> + Â Â Â if (!rfkill_dev)
> + Â Â Â Â Â Â Â return ERR_PTR(-ENOMEM);
> +
> + Â Â Â err = rfkill_register(rfkill_dev);

Maybe you should add a rfkill_init_sw_state() call here, to be sure
that the rfkill initial state is right.

> + Â Â Â if (err) {
> + Â Â Â Â Â Â Â rfkill_destroy(rfkill_dev);
> + Â Â Â Â Â Â Â return ERR_PTR(err);
> + Â Â Â }
> + Â Â Â return rfkill_dev;
> +}
> +
> +static inline void _oaktrail_rfkill_cleanup(struct rfkill *rf)

I don't know if the coding style allow _ function prefix, I used to
believe it did
not, but I checked Documentation/CodingStyle, and I found nothing about that,
so it's probably ok.

> +{
> + Â Â Â if (rf) {
> + Â Â Â Â Â Â Â rfkill_unregister(rf);
> + Â Â Â Â Â Â Â rfkill_destroy(rf);
> + Â Â Â }
> +
> + Â Â Â return;

We don't need that return

> +}
> +
> +static void oaktrail_rfkill_cleanup(void)
> +{
> + Â Â Â _oaktrail_rfkill_cleanup(wifi_rfkill);
> + Â Â Â _oaktrail_rfkill_cleanup(bt_rfkill);
> + Â Â Â _oaktrail_rfkill_cleanup(gps_rfkill);
> + Â Â Â _oaktrail_rfkill_cleanup(wwan_rfkill);
> +
> + Â Â Â return;

And that one

> +}
> +
> +static int oaktrail_rfkill_init(void)
> +{
> + Â Â Â int ret;
> +
> + Â Â Â wifi_rfkill = oaktrail_rfkill_new("oaktrail-wifi",
> + Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â RFKILL_TYPE_WLAN,
> + Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â OT_EC_WIFI_MASK);
> + Â Â Â if (IS_ERR(wifi_rfkill)) {
> + Â Â Â Â Â Â Â ret = PTR_ERR(wifi_rfkill);
> + Â Â Â Â Â Â Â wifi_rfkill = NULL;
> + Â Â Â Â Â Â Â goto cleanup;
> + Â Â Â }
> +
> + Â Â Â bt_rfkill = oaktrail_rfkill_new("oaktrail-bluetooth",
> + Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â RFKILL_TYPE_BLUETOOTH,
> + Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â OT_EC_BT_MASK);
> + Â Â Â if (IS_ERR(bt_rfkill)) {
> + Â Â Â Â Â Â Â ret = PTR_ERR(bt_rfkill);
> + Â Â Â Â Â Â Â bt_rfkill = NULL;
> + Â Â Â Â Â Â Â goto cleanup;
> + Â Â Â }
> +
> + Â Â Â gps_rfkill = oaktrail_rfkill_new("oaktrail-gps",
> + Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â ÂRFKILL_TYPE_GPS,
> + Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â ÂOT_EC_GPS_MASK);
> + Â Â Â if (IS_ERR(gps_rfkill)) {
> + Â Â Â Â Â Â Â ret = PTR_ERR(gps_rfkill);
> + Â Â Â Â Â Â Â gps_rfkill = NULL;
> + Â Â Â Â Â Â Â goto cleanup;
> + Â Â Â }
> +
> + Â Â Â wwan_rfkill = oaktrail_rfkill_new("oaktrail-wwan",
> + Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â RFKILL_TYPE_WWAN,
> + Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â OT_EC_WWAN_MASK);
> + Â Â Â if (IS_ERR(wwan_rfkill)) {
> + Â Â Â Â Â Â Â ret = PTR_ERR(wwan_rfkill);
> + Â Â Â Â Â Â Â wwan_rfkill = NULL;
> + Â Â Â Â Â Â Â goto cleanup;
> + Â Â Â }
> +
> + Â Â Â return 0;
> +
> +cleanup:
> + Â Â Â oaktrail_rfkill_cleanup();
> + Â Â Â return ret;
> +}
> +
> +static int __devinit oaktrail_probe(struct platform_device *pdev)
> +{
> + Â Â Â return sysfs_create_group(&pdev->dev.kobj, &oaktrail_attribute_group);
> +}
> +
> +static int __devexit oaktrail_remove(struct platform_device *pdev)
> +{
> + Â Â Â sysfs_remove_group(&pdev->dev.kobj, &oaktrail_attribute_group);
> +
> + Â Â Â return 0;
> +}
> +
> +static struct platform_driver oaktrail_driver = {
> + Â Â Â .driver = {
> + Â Â Â Â Â Â Â .name = DRIVER_NAME,
> + Â Â Â Â Â Â Â .owner = THIS_MODULE,
> + Â Â Â },
> + Â Â Â .probe Â= oaktrail_probe,
> + Â Â Â .remove = __devexit_p(oaktrail_remove)
> +};
> +
> +static int dmi_check_cb(const struct dmi_system_id *id)
> +{
> + Â Â Â pr_info("Identified model '%s'\n", id->ident);
> + Â Â Â return 0;
> +}
> +
> +static struct dmi_system_id __initdata oaktrail_dmi_table[] = {
> + Â Â Â {
> + Â Â Â Â Â Â Â .ident = "OakTrail platform",
> + Â Â Â Â Â Â Â .matches = {
> + Â Â Â Â Â Â Â Â Â Â Â DMI_MATCH(DMI_PRODUCT_NAME, "OakTrail platform"),
> + Â Â Â Â Â Â Â },
> + Â Â Â Â Â Â Â .callback = dmi_check_cb
> + Â Â Â },
> + Â Â Â { }
> +};
> +
> +static int __init oaktrail_init(void)
> +{
> + Â Â Â int ret;
> +
> + Â Â Â if (acpi_disabled) {
> + Â Â Â Â Â Â Â pr_err("ACPI needs to be enabled for this driver to work!\n");
> + Â Â Â Â Â Â Â return -ENODEV;
> + Â Â Â }
> +
> + Â Â Â if (!force && !dmi_check_system(oaktrail_dmi_table)) {
> + Â Â Â Â Â Â Â pr_err("Platform not recognized (You could try the module's force-parameter)");
> + Â Â Â Â Â Â Â return -ENODEV;
> + Â Â Â }
> +
> + Â Â Â ret = platform_driver_register(&oaktrail_driver);
> + Â Â Â if (ret) {
> + Â Â Â Â Â Â Â pr_warning("Unable to register platform driver\n");
> + Â Â Â Â Â Â Â goto err_driver_reg;
> + Â Â Â }
> +
> + Â Â Â oaktrail_device = platform_device_alloc(DRIVER_NAME, -1);
> + Â Â Â if (!oaktrail_device) {
> + Â Â Â Â Â Â Â pr_warning("Unable to allocate platform device\n");
> + Â Â Â Â Â Â Â ret = -ENOMEM;
> + Â Â Â Â Â Â Â goto err_device_alloc;
> + Â Â Â }
> +
> + Â Â Â ret = platform_device_add(oaktrail_device);
> + Â Â Â if (ret) {
> + Â Â Â Â Â Â Â pr_warning("Unable to add platform device\n");
> + Â Â Â Â Â Â Â goto err_device_add;
> + Â Â Â }
> +
> + Â Â Â ret = oaktrail_rfkill_init();
> + Â Â Â if (ret) {
> + Â Â Â Â Â Â Â pr_warning("Setup rfkill failed\n");
> + Â Â Â Â Â Â Â goto err_rfkill;
> + Â Â Â }
> +
> + Â Â Â pr_info("Driver "DRIVER_VERSION" successfully loaded\n");
> + Â Â Â return 0;
> +
> +err_rfkill:
> + Â Â Â platform_device_del(oaktrail_device);
> +err_device_add:
> + Â Â Â platform_device_put(oaktrail_device);
> +err_device_alloc:
> + Â Â Â platform_driver_unregister(&oaktrail_driver);
> +err_driver_reg:
> + Â Â Â return ret;
> +}
> +
> +static void __exit oaktrail_cleanup(void)
> +{
> + Â Â Â platform_device_unregister(oaktrail_device);
> + Â Â Â platform_driver_unregister(&oaktrail_driver);
> + Â Â Â oaktrail_rfkill_cleanup();
> +
> + Â Â Â pr_info("Driver unloaded\n");
> +}
> +
> +module_init(oaktrail_init);
> +module_exit(oaktrail_cleanup);
> +
> +MODULE_AUTHOR("Yin Kangkai (kangkai.yin@xxxxxxxxx)");
> +MODULE_DESCRIPTION("Intel Oaktrail Platform ACPI Extras");
> +MODULE_VERSION(DRIVER_VERSION);
> +MODULE_LICENSE("GPL");
> +MODULE_ALIAS("dmi:*:svnIntelCorporation:pnOakTrailplatform:*");
> --
> 1.6.5
>
>

--
Corentin Chary
http://xf.iksaif.net
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/