Skip to contentSkip to conversation

misc: Add oqo-wmi driver for model 2 OQO backlight and rfkill control

PATCH In review Matthew Garrett 5 participants 12 messages Nov 27, 2008 – Jan 9, 2009

Subsystems (3)
lore
Share & tools
lore
#0 lore raw

PATCH 1/1 misc: Add oqo-wmi driver for model 2 OQO backlight and rfkill control

misc: Add oqo-wmi driver for model 2 OQO backlight and rfkill control

oqo-wmi provides a WMI-based interface to backlight and rfkill control on model 2 OQO devices. It was originally written by Brian Julin (<[email protected]>) - I've ported it to the rfkill layer and tidied it up a little.

drivers/misc/Kconfig +13 -0
index 3949a1c..b7af402 100644
@@ -237,6 +237,19 @@ config ICS932S401
 	  This driver can also be built as a module. If so, the module
 	  will be called ics932s401.
 
+config OQO_WMI
+       tristate "OQO WMI extras"
+       depends on ACPI_WMI
+       depends on INPUT
+       depends on RFKILL
+       depends on BACKLIGHT_CLASS_DEVICE
+       help
+         Say Y here if you want to support rfkill and backlight control on
+         series 2 OQO handheld devices.
+
+         To compile this driver as a module, choose M here: the module will
+         be called oqo-wmi.
+
 config MSI_LAPTOP
         tristate "MSI Laptop Extras"
         depends on X86
drivers/misc/Makefile +1 -0
index 39043f8..1109062 100644
@@ -16,6 +16,7 @@ obj-$(CONFIG_ATMEL_TCLIB)	+= atmel_tclib.o
 obj-$(CONFIG_HP_WMI)		+= hp-wmi.o
 obj-$(CONFIG_ICS932S401)	+= ics932s401.o
 obj-$(CONFIG_TC1100_WMI)	+= tc1100-wmi.o
+obj-$(CONFIG_OQO_WMI)		+= oqo-wmi.o
 obj-$(CONFIG_LKDTM)		+= lkdtm.o
 obj-$(CONFIG_TIFM_CORE)       	+= tifm_core.o
 obj-$(CONFIG_TIFM_7XX1)       	+= tifm_7xx1.o
drivers/misc/oqo-wmi.c new file +941 -0

941 lines not on this page · raw message

Signature · 1 line
Matthew Garrett | [email protected]
#2 lore raw
Quoted 1 line · Len Brown

needs a MAINTAINERS entry.

If it were to be set to me it would have to be S: Odd Fixes. I don't know if this is on Matthew's redhat-official "pile of laptops"...

Either way I've marked the webpage hosting the original code as depricated and added a link to linux-acpi's home. I'll have to test the new patch too at some point but not this weekend.

--
Brian

#3 lore raw
Quoted 6 lines · Brian S. Julin
Quoted 1 line · Len Brown

needs a MAINTAINERS entry.

If it were to be set to me it would have to be S: Odd Fixes. I don't know if this
is on Matthew's redhat-official "pile of laptops"...

Somewhat. There's some work to be done on getting X to work out the LVDS sizing correctly, but other than that I think I get to play with it for now...

Quoted 3 lines

Either way I've marked the webpage hosting the original code as depricated and added a link to linux-acpi's home. I'll have to test the new patch too at some point but not this weekend.

Cool, thanks. I'll have more time to work on the hardware the week after next.

Signature · 1 line
Matthew Garrett | [email protected]
#4 lore raw

PATCH 1/1 misc: Add oqo-wmi driver for model 2 OQO backlight and rfkill control

oqo-wmi provides a WMI-based interface to backlight and rfkill control on model 2 OQO devices. It was originally written by Brian Julin (<[email protected]>) - I've ported it to the rfkill layer and tidied it up a little.

---

I've added a MAINTAINERS entry - I'm happy to look after this for now, if Brian doesn't have any objections.

MAINTAINERS +5 -0
index 618c1ef..c76ef2d 100644
@@ -3233,6 +3233,11 @@ L:	[email protected]
 W:	http://www.nongnu.org/orinoco/
 S:	Maintained
 
+OQO WMI EXTRAS DRIVER
+P:	Matthew Garrett
+M:	[email protected]
+S:	Maintained
+
 P54 WIRELESS DRIVER
 P:	Michael Wu
 M:	[email protected]
drivers/misc/Kconfig +13 -0
index 3949a1c..b7af402 100644
@@ -237,6 +237,19 @@ config ICS932S401
 	  This driver can also be built as a module. If so, the module
 	  will be called ics932s401.
 
+config OQO_WMI
+       tristate "OQO WMI extras"
+       depends on ACPI_WMI
+       depends on INPUT
+       depends on RFKILL
+       depends on BACKLIGHT_CLASS_DEVICE
+       help
+         Say Y here if you want to support rfkill and backlight control on
+         series 2 OQO handheld devices.
+
+         To compile this driver as a module, choose M here: the module will
+         be called oqo-wmi.
+
 config MSI_LAPTOP
         tristate "MSI Laptop Extras"
         depends on X86
drivers/misc/Makefile +1 -0
index 39043f8..1109062 100644
@@ -16,6 +16,7 @@ obj-$(CONFIG_ATMEL_TCLIB)	+= atmel_tclib.o
 obj-$(CONFIG_HP_WMI)		+= hp-wmi.o
 obj-$(CONFIG_ICS932S401)	+= ics932s401.o
 obj-$(CONFIG_TC1100_WMI)	+= tc1100-wmi.o
+obj-$(CONFIG_OQO_WMI)		+= oqo-wmi.o
 obj-$(CONFIG_LKDTM)		+= lkdtm.o
 obj-$(CONFIG_TIFM_CORE)       	+= tifm_core.o
 obj-$(CONFIG_TIFM_7XX1)       	+= tifm_7xx1.o
drivers/misc/oqo-wmi.c new file +941 -0

941 lines not on this page · raw message

Signature · 1 line
Matthew Garrett | [email protected]
#5 lore raw
Quoted 4 lines · Matthew Garrett

oqo-wmi provides a WMI-based interface to backlight and rfkill control on model 2 OQO devices. It was originally written by Brian Julin (<[email protected]>) - I've ported it to the rfkill layer and tidied it up a little.

Some general comments below.

Quoted 12 lines
drivers/misc/oqo-wmi.c new file +12 -0
+/* Some of this code is left like in acer-wmi so we can add the older
+   Model 01 and any future models more easily, but we should not expect
+   it to be as complicated as Acer given each model is a leap rather than
+   a subtle variant on the last, so we aren't using "quirks" perse.  Not
+   sure if there is any real difference for our purposes between the o2
+   and e2.
+*/
+struct oqo_model {
+	const char *model;
+	u16 model_subs;
+};
+#define MODEL_SUB_OQO_O2_SMB0 3

drivers/misc/oqo-wmi.c line 103

Quoted 10 lines
drivers/misc/oqo-wmi.c new file +10 -0
+
+static struct oqo_model oqo_models[] = {
+	{
+	 .model = "Model 2",
+	 .model_subs = MODEL_SUB_OQO_O2_SMB0,
+	 },
+	{}
+};
+
+static struct oqo_model *model;

With "like in acer-wmi" you mean that this whole model stuff currently serves no purpose? If we don't access the model variable yet, I think it shouldn't be there.

Quoted 1 line

+static int dmi_matched(const struct dmi_system_id *dmi)

__init

drivers/misc/oqo-wmi.c line 193

Quoted 12 lines
drivers/misc/oqo-wmi.c new file +12 -0
+{
+	model = dmi->driver_data;
+	/*
+	 * Detect which ACPI-WMI interface we're using.
+	 */
+	if (wmi_has_guid(OQO_O2_AMW0_GUID))
+		interface = &AMW0_interface;
+
+	return 0;
+}
+
+static struct dmi_system_id oqo_dmis[] = {

__initdata

And i think const is also possible.

Quoted 5 lines
drivers/misc/oqo-wmi.c new file +5 -0
+static acpi_status oqo_read_kine(int *good, s16 *x, s16 *y, s16 *z,
+				 u16 *lumin)
+{
+	u8 hiregs[4] = { OQO_O2_SMB0_ACCEL_XHI,
+		OQO_O2_SMB0_ACCEL_YHI,

drivers/misc/oqo-wmi.c line 480

Quoted 10 lines
drivers/misc/oqo-wmi.c new file +10 -0
+		OQO_O2_SMB0_ACCEL_ZHI,
+		OQO_O2_SMB0_LUMIN_HI
+	};
+	u8 loregs[4] = { OQO_O2_SMB0_ACCEL_XLO,
+		OQO_O2_SMB0_ACCEL_YLO,
+		OQO_O2_SMB0_ACCEL_ZLO,
+		OQO_O2_SMB0_LUMIN_LO
+	};
+
+	short ax[4] = { ABS_X, ABS_Y, ABS_Z, ABS_MISC };

Uhm, I think these three variables could be static and const.

drivers/misc/oqo-wmi.c line 544

Quoted 5 lines
drivers/misc/oqo-wmi.c new file +5 -0
+	*x = (u16) res[0];
+	*y = (u16) res[1];
+	*z = (u16) res[2];
+	*lumin = (u16) res[3];
+	return status;

This return could be dropped, just let it fall-through.

Quoted 33 lines
drivers/misc/oqo-wmi.c new file +30 -0
+leave:
+	return status;
+}
 
 
 
+static int __devinit oqo_kine_init(void)
+{
+	int err;
+
+	oqo_kine = input_allocate_device();
+	if (!oqo_kine)
+		return -ENOMEM;
+
+	oqo_kine->name = "OQO embedded accelerometer";
+	oqo_kine->phys = "platform:oqo-wmi:kine";
+	oqo_kine->id.bustype = 0;
+	oqo_kine->id.vendor = 0;
+	oqo_kine->id.product = 2;
+	oqo_kine->id.version = 0;
+	oqo_kine->evbit[0] = BIT_MASK(EV_ABS);
+	set_bit(ABS_X, oqo_kine->absbit);
+	set_bit(ABS_Y, oqo_kine->absbit);
+	set_bit(ABS_Z, oqo_kine->absbit);
+	set_bit(ABS_MISC, oqo_kine->absbit);
+	oqo_kine->absmin[ABS_X] =
+	    oqo_kine->absmin[ABS_Y] =
+	    oqo_kine->absmin[ABS_Z] = oqo_kine->absmin[ABS_MISC] = -32768;
+	oqo_kine->absmax[ABS_X] =
+	    oqo_kine->absmax[ABS_Y] =
+	    oqo_kine->absmax[ABS_Z] = oqo_kine->absmax[ABS_MISC] = 32767;
+
+	memcpy(oqo_kine->dev.bus_id, "kine", 4);

drivers/misc/oqo-wmi.c line 605

Quoted 10 lines
drivers/misc/oqo-wmi.c new file +10 -0
+
+	oqo_kine_polled = input_allocate_polled_device();
+	if (!oqo_kine_polled) {
+		err = -ENOMEM;
+		goto bail0;
+	}
+
+	oqo_kine_polled->poll = oqo_kine_poll;
+	oqo_kine_polled->poll_interval = 250;
+	oqo_kine_polled->input = oqo_kine;

input_allocate_polled_device() allocates ->input for us, you must remove the input_allocate_device() above and shift the code around.

drivers/misc/oqo-wmi.c line 642

Quoted 5 lines
drivers/misc/oqo-wmi.c new file +5 -0
+static void __devexit oqo_kine_fini(void)
+{
+	smwrite_u8(OQO_O2_SMB0_ACCEL_POLL_ITVL, orig.kine_itvl);
+	input_unregister_polled_device(oqo_kine_polled);
+	input_free_polled_device(oqo_kine_polled);

Uhm, I wonder if this is the right order. First unregister and then do the smwrite_u8() to prevent the device from reverting our write?

drivers/misc/oqo-wmi.c line 792

Quoted 5 lines
drivers/misc/oqo-wmi.c new file +5 -0
+static int __devinit oqo_platform_probe(struct platform_device *device)
+{
+	int err;
+	int i;
+	char *troubleok = "trouble, but continuing.\n";

This variable seems needless, just append the message to the printk() calls.

Quoted 24 lines
drivers/misc/oqo-wmi.c new file +24 -0
+
+	memset(oqo_sn, 0, OQO_O2_SMB0_SERIAL_LEN + 1);
+	for (i = 0; i < OQO_O2_SMB0_SERIAL_LEN; i++) {
+		err = oqo_smbus_getb(OQO_O2_SMB0_SERIAL_START + i, oqo_sn + i);
+		if (err) {
+			printk(OQO_ERR "Serial number check failed.\n");
+			return err;
+		}
+	}
+	printk(OQO_INFO "Found OQO with serial number %s.\n", oqo_sn);
+
+	err = oqo_backlight_init(&device->dev);
+	if (err)
+		printk(OQO_ERR "Backlight init %s", troubleok);
+
+	err = oqo_rfkill_init(&device->dev);
+	if (err)
+		printk(OQO_ERR "RFKill init %s", troubleok);
+
+	/* LID does not work at all yet, and this may be taken
+	   care of by ACPI.
+	 */
+	orig.lid_wakes = smread_u8(OQO_O2_SMB0_LID_WAKES_ADDR);
+	orig.lid_wakes &= OQO_O2_SMB0_LID_WAKES_MASK;

drivers/misc/oqo-wmi.c line 826

Quoted 10 lines
drivers/misc/oqo-wmi.c new file +10 -0
+	orig.lid_wakes = curr.lid_wakes = !!orig.lid_wakes;
+	if (orig.lid_wakes < 0) {
+		printk(OQO_ERR "Wake on LID event %s", troubleok);
+	} else {
+		printk(OQO_INFO "Wake on LID is %s.\n",
+		       (orig.lid_wakes ? "on" : "off"));
+	}
+
+	err = oqo_kine_init();
+	return err;

Uhm, if oqo_kine_init() fails, does our caller take care of unregistering the backlight and rfkill devices?

drivers/misc/oqo-wmi.c line 833

Quoted 7 lines
drivers/misc/oqo-wmi.c new file +7 -0
+}
+
+static int oqo_platform_remove(struct platform_device *device)
+{
+	oqo_backlight_fini();
+	oqo_rfkill_fini();
+	oqo_kine_fini();

These three functions are marked __devexit, I suspect this will cause section mismatches.

drivers/misc/oqo-wmi.c line 843

Quoted 10 lines
drivers/misc/oqo-wmi.c new file +10 -0
+
+	return 0;
+}
+
+#ifdef CONFIG_PM
+
+static int oqo_platform_suspend(struct platform_device *dev, pm_message_t state)
+{
+	if (!interface)
+		return -ENOMEM;

Interface should be always set once the module has been loaded successfully.

drivers/misc/oqo-wmi.c line 856

Quoted 13 lines
drivers/misc/oqo-wmi.c new file +13 -0
+
+	/* This sticks during boot so do not turn it entirely off */
+	if (oqo_backlight_device) {
+		curr.bl_bright = read_brightness(oqo_backlight_device);
+		smwrite_s16(OQO_O2_SMB0_BL_HI, OQO_O2_SMB0_BL_LO, 256);
+	}
+	return 0;
+}
+
+static int oqo_platform_resume(struct platform_device *device)
+{
+	if (!interface)
+		return -ENOMEM;

Likewise.

Quoted 4 lines
drivers/misc/oqo-wmi.c new file +4 -0
+
+	if (oqo_backlight_device) {
+		smwrite_s16(OQO_O2_SMB0_BL_HI,
+			    OQO_O2_SMB0_BL_LO, curr.bl_bright);

drivers/misc/oqo-wmi.c line 921

Quoted 10 lines
drivers/misc/oqo-wmi.c new file +9 -0
+	}
+
+	return 0;
+}
 
+	err = platform_device_add(oqo_platform_device);
+	if (err) {
+		printk(OQO_ERR "platform_device_add gave %d.\n", err);
+		platform_device_put(oqo_platform_device);
+		goto bail1;

Why not add a bail2 and move platform_device_put() there?

Quoted 7 lines
drivers/misc/oqo-wmi.c new file +7 -0
+	}
+
+	return 0;
+
+bail1:
+	platform_driver_unregister(&oqo_platform_driver);
+bail0:

drivers/misc/oqo-wmi.c line 938

Quoted 10 lines
drivers/misc/oqo-wmi.c new file +10 -0
+	return err;
+}
+
+static void __exit oqo_wmi_fini(void)
+{
+	platform_device_del(oqo_platform_device);
+	platform_driver_unregister(&oqo_platform_driver);
+
+	return;
+}

Needless return at end of function.

Sven

#6 lore raw
In reply to Matthew Garrett
Quoted 7 lines · On Wed, 3 Dec 2008 19:55:37 +0000 Matthew Garrett <[email protected]> wrote:

oqo-wmi provides a WMI-based interface to backlight and rfkill control on model 2 OQO devices. It was originally written by Brian Julin (<[email protected]>) - I've ported it to the rfkill layer and tidied it up a little.

Are the attributions and signoffs appropriate here?

drivers/misc/oqo-wmi.c line 55

Quoted 6 lines

...

drivers/misc/oqo-wmi.c new file +3 -0
+#include <acpi/acpi_drivers.h>
+
+MODULE_AUTHOR("Brian Julin");

Add yourself?

Quoted 36 lines
drivers/misc/oqo-wmi.c new file +36 -0
+MODULE_DESCRIPTION("OQO UPMC WMI Extras Driver");
+MODULE_LICENSE("GPL");
+
+#define OQO_LOGPREFIX "oqo-wmi: "
+#define OQO_ERR KERN_ERR OQO_LOGPREFIX
+#define OQO_NOTICE KERN_NOTICE OQO_LOGPREFIX
+#define OQO_INFO KERN_INFO OQO_LOGPREFIX
+
+#define OQO_KINE_MAXTRY 3
+
+/* Store defined devices globally since we only have one instance. */
+static struct platform_device *oqo_platform_device;
+static struct backlight_device *oqo_backlight_device;
+static struct rfkill *oqo_rfkill;
+static struct input_dev *oqo_kine;
+static struct input_polled_dev *oqo_kine_polled;
+
+/* Likewise store current and original settings globally. */
+struct oqo_settings {
+	int lid_wakes;		/* not sure if ACPI handles/needs help here */
+	int kine_itvl;
+	int bl_bright;
+};
+
+static struct oqo_settings orig, curr;
+
+/* Some of this code is left like in acer-wmi so we can add the older
+   Model 01 and any future models more easily, but we should not expect
+   it to be as complicated as Acer given each model is a leap rather than
+   a subtle variant on the last, so we aren't using "quirks" perse.  Not
+   sure if there is any real difference for our purposes between the o2
+   and e2.
+*/
+struct oqo_model {
+	const char *model;
+	u16 model_subs;

drivers/misc/oqo-wmi.c line 101

Quoted 10 lines
drivers/misc/oqo-wmi.c new file +10 -0
+};
+#define MODEL_SUB_OQO_O2_SMB0 3
+
+static struct oqo_model oqo_models[] = {
+	{
+	 .model = "Model 2",
+	 .model_subs = MODEL_SUB_OQO_O2_SMB0,
+	 },
+	{}
+};

Might be able to make this and oqo_dmis[] const. Although that tends to be a pita, easily ignorable..

Quoted 6 lines
drivers/misc/oqo-wmi.c new file +3 -0
+static struct oqo_model *model;
+
 
...
 
+/*

drivers/misc/oqo-wmi.c line 171

Quoted 10 lines
drivers/misc/oqo-wmi.c new file +10 -0
+ * Interface type flags
+ */
+enum interface_type {
+	OQO_O2_AMW0,
+};
+
+/* Each low-level interface must define at least some of the following */
+struct wmi_interface {
+	/* The WMI device type */
+	u32 type;

Can have type `enum interface_type'.

Quoted 21 lines
drivers/misc/oqo-wmi.c new file +18 -0
+};
+
+static struct wmi_interface AMW0_interface = {
+	.type = OQO_O2_AMW0,
+};
+
 
...
 
+static acpi_status oqo_smbus_getb(u8 addr, u8 *result)
+{
+	struct acpi_buffer input, res;
+	acpi_status status;
+	union acpi_object *obj;
+	u32 arg2;
+
+	input.length = 4;
+	input.pointer = &arg2;
+	res.length = ACPI_ALLOCATE_BUFFER;
+	res.pointer = NULL;
+

drivers/misc/oqo-wmi.c line 242

Quoted 10 lines
drivers/misc/oqo-wmi.c new file +10 -0
+	arg2 = addr;
+	arg2 <<= 8;
+	arg2 |= 0x12;		/* HOSTCMD */
+
+	status = wmi_evaluate_method(OQO_O2_AMW0_GUID, 1, 1, &input, &res);
+
+	if (status != AE_OK)
+		return status;
+
+	obj = (union acpi_object *)res.pointer;

Unneeded and undesirable cast of void*.

Review

Quoted 2 lines
(quoted diff) +2 -0
+	if (!obj)
+		return AE_NULL_OBJECT;

AE_NO_MEMORY, perhaps.

drivers/misc/oqo-wmi.c line 249

Quoted 4 lines
drivers/misc/oqo-wmi.c new file +4 -0
+	if (obj->type != ACPI_TYPE_BUFFER
+	    || obj->buffer.length != 1 || obj->buffer.pointer == NULL) {
+		kfree(obj);
+		return AE_TYPE;

I wish someone had documented all these in include/acpi/acexcep.h.

Quoted 20 lines
drivers/misc/oqo-wmi.c new file +20 -0
+	}
+	*result = ((u8 *) (obj->buffer.pointer))[0];
+	kfree(obj);
+	return status;
+}
+
+static acpi_status oqo_smbus_setb(u8 addr, u8 val)
+{
+	struct acpi_buffer input, res;
+	acpi_status status;
+	union acpi_object *obj;
+	u32 arg2;
+
+	input.length = 4;
+	input.pointer = &arg2;
+	res.length = ACPI_ALLOCATE_BUFFER;
+	res.pointer = NULL;
+
+	arg2 = val;
+	arg2 <<= 8;

drivers/misc/oqo-wmi.c line 279

Quoted 10 lines
drivers/misc/oqo-wmi.c new file +10 -0
+	arg2 |= addr;
+	arg2 <<= 8;
+	arg2 |= 0x12;		/* HOSTCMD */
+
+	status = wmi_evaluate_method(OQO_O2_AMW0_GUID, 1, 2, &input, &res);
+
+	if (status != AE_OK)
+		return status;
+
+	obj = (union acpi_object *)res.pointer;

unneeded cast

Review

Quoted 2 lines
(quoted diff) +2 -0
+	if (!obj)
+		return AE_NULL_OBJECT;

AE_NO_MEMORY?

Quoted 41 lines
drivers/misc/oqo-wmi.c new file +41 -0
+	if (obj->type != ACPI_TYPE_INTEGER) {
+		kfree(obj);
+		return AE_TYPE;
+	}
+	kfree(obj);
+	return status;
+}
+
+/*
+ * We assume we are the only one using this ...ahem... "lock" on
+ * the SMBUS because it would be pathetically noneffective otherwise.
+ *
+ * Nonzero silly_lock will keep certain ACPI routines away from the
+ * SMBUS (if they aren't already on it when you call it.)  Zero
+ *  silly_lock will let them back on
+ *
+ * This is probably useful before sleeping the system, and one
+ * waits until any ACPI funcs would have long finished before
+ * proceeding.  It seems harmless enough and will work to wrap
+ * more accesses with it.
+ */
+static acpi_status oqo_lock_smbus(int silly_lock)
+{
+	struct acpi_buffer input, res;
+	acpi_status status;
+	union acpi_object *obj;
+	u32 arg2;
+
+	input.length = 4;
+	input.pointer = &arg2;
+	res.length = ACPI_ALLOCATE_BUFFER;
+	res.pointer = NULL;
+
+	arg2 = !!silly_lock;
+
+	status = wmi_evaluate_method(OQO_O2_AMW0_GUID, 1, 4, &input, &res);
+
+	if (status != AE_OK)
+		return status;
+
+	obj = (union acpi_object *)res.pointer;

drivers/misc/oqo-wmi.c line 333

Quoted 10 lines
drivers/misc/oqo-wmi.c new file +10 -0
+	if (!obj)
+		return AE_NULL_OBJECT;
+
+	if (obj->type != ACPI_TYPE_INTEGER) {
+		kfree(obj);
+		return AE_TYPE;
+	}
+	kfree(obj);
+	return status;
+}

dittoes

drivers/misc/oqo-wmi.c line 344

Quoted 10 lines
drivers/misc/oqo-wmi.c new file +10 -0
+static int smread_s16(u8 hi_addr, u8 lo_addr)
+{
+	s16 ret = -1;
+	acpi_status status;
+	u8 r;
+
+	/* Keep some ACPI routines off the SMBUS */
+	status = oqo_lock_smbus(1);
+	if (ACPI_FAILURE(status))
+		goto skip;

Does this mean that we didn't take the lock? If so, will running oqo_lock_smbus(0) be correct?

Quoted 22 lines
drivers/misc/oqo-wmi.c new file +22 -0
+	status = oqo_smbus_getb(hi_addr, &r);
+	if (ACPI_FAILURE(status))
+		goto skip;
+
+	ret = r;
+	ret <<= 8;
+
+	status = oqo_smbus_getb(lo_addr, &r);
+	if (ACPI_FAILURE(status)) {
+		ret = -1;
+		goto skip;
+	}
+
+	ret |= r;
+	ret &= 0x7fff;
+skip:
+	/* Let ACPI routines back on the SMBUS */
+	status = oqo_lock_smbus(0);
+	if (ACPI_FAILURE(status))
+		return -1;
+	return (int)ret;
+}

drivers/misc/oqo-wmi.c line 377

Quoted 10 lines
drivers/misc/oqo-wmi.c new file +10 -0
+
+static int smwrite_s16(u8 hi_addr, u8 lo_addr, s16 val)
+{
+	acpi_status status;
+	u8 r;
+	int ret = -1;
+
+	status = oqo_lock_smbus(1);
+	if (ACPI_FAILURE(status))
+		goto skip;

Ditto

Quoted 17 lines
drivers/misc/oqo-wmi.c new file +17 -0
+	r = (val >> 8) & 0x7f;
+	status = oqo_smbus_setb(hi_addr, r);
+	if (ACPI_FAILURE(status))
+		goto skip;
+
+	r = val & 0xff;
+	status = oqo_smbus_setb(lo_addr, r);
+	if (ACPI_FAILURE(status))
+		goto skip;
+
+	ret = 0;
+skip:
+	status = oqo_lock_smbus(0);
+	if (ACPI_FAILURE(status))
+		return -1;
+	return ret;
+}

drivers/misc/oqo-wmi.c line 405

Quoted 10 lines
drivers/misc/oqo-wmi.c new file +10 -0
+
+static int smread_u8(u8 addr)
+{
+	int ret = -1;
+	acpi_status status;
+	u8 r;
+
+	status = oqo_lock_smbus(1);
+	if (ACPI_FAILURE(status))
+		goto skip;

etc..

Quoted 20 lines
drivers/misc/oqo-wmi.c new file +17 -0
+	status = oqo_smbus_getb(addr, &r);
+	if (ACPI_FAILURE(status))
+		goto skip;
+
+	ret = r;
+skip:
+	status = oqo_lock_smbus(0);
+	if (ACPI_FAILURE(status))
+		return -1;
+	return (int)ret;
+}
+
 
...
 
+static acpi_status oqo_read_kine(int *good, s16 *x, s16 *y, s16 *z,
+				 u16 *lumin)
+{
+	u8 hiregs[4] = { OQO_O2_SMB0_ACCEL_XHI,
+		OQO_O2_SMB0_ACCEL_YHI,

drivers/misc/oqo-wmi.c line 480

Quoted 10 lines
drivers/misc/oqo-wmi.c new file +10 -0
+		OQO_O2_SMB0_ACCEL_ZHI,
+		OQO_O2_SMB0_LUMIN_HI
+	};
+	u8 loregs[4] = { OQO_O2_SMB0_ACCEL_XLO,
+		OQO_O2_SMB0_ACCEL_YLO,
+		OQO_O2_SMB0_ACCEL_ZLO,
+		OQO_O2_SMB0_LUMIN_LO
+	};
+
+	short ax[4] = { ABS_X, ABS_Y, ABS_Z, ABS_MISC };

The above arrays will be assembled on the stack each time this function is called (unless the compiler is being particularly smart). If there is a way of making them static const, goodness will ensue.

Quoted 22 lines
drivers/misc/oqo-wmi.c new file +22 -0
+	u8 realgood = 0;
+	u16 res[4];
+	acpi_status status;
+	int i;
+
+	*good = 0;
+
+	/* Routine: Starting with the lo byte, read lo/hi bytes
+	   alternately until two lo byte readings, match.  Then
+	   take that reading and combine it with the hi reading
+	   sandwiched between.  Errors can still happen when
+	   jittering at wrap boundaries, but should be rare.
+
+	   Don't use this for missile guidance.
+
+	   Userspace post-processing error detection encouraged.
+	 */
+	for (i = 0; i < 4; i++) {
+		int maxtry;
+		u32 log;
+		u8 r, lo, hi;
+

drivers/misc/oqo-wmi.c line 512

Quoted 10 lines
drivers/misc/oqo-wmi.c new file +10 -0
+		lo = loregs[i];
+		hi = hiregs[i];
+		log = 0;
+
+#define LOGRES(reg)  do { \
+			status = oqo_smbus_getb(reg, &r);		\
+			log <<= 8; log |= r; log &= 0xffffff;		\
+			if (ACPI_FAILURE(status))			\
+				goto leave;				\
+		} while (0)

eww...

Quoted 20 lines
drivers/misc/oqo-wmi.c new file +20 -0
+		maxtry = OQO_KINE_MAXTRY + 1;
+		while (maxtry) {
+			LOGRES(lo);
+			if (maxtry <= OQO_KINE_MAXTRY &&
+			    (log >> 16) == (log & 0xff)) {
+				*(res + i) = log & 0xffff;
+				break;
+			}
+			LOGRES(hi);
+			maxtry--;
+		}
+
+		if (maxtry == OQO_KINE_MAXTRY)
+			realgood |= 1 << i;
+
+		if (maxtry) {
+			*good |= 1 << i;
+			/* JIC CYA: this bit may be reserved */
+			res[3] &= 0x7fff;
+			input_report_abs(oqo_kine, ax[i], (s16) res[i]);

drivers/misc/oqo-wmi.c line 543

Quoted 10 lines
drivers/misc/oqo-wmi.c new file +10 -0
+		}
+		/* else we had trouble getting the reading to lock
+		   and we skip reporting this axis.
+		 */
+	}
+
+	*x = (u16) res[0];
+	*y = (u16) res[1];
+	*z = (u16) res[2];
+	*lumin = (u16) res[3];

Those four casts are unneeded.

Quoted 5 lines
drivers/misc/oqo-wmi.c new file +5 -0
+	return status;
+leave:
+	return status;
+}
+

drivers/misc/oqo-wmi.c line 558

Quoted 10 lines
drivers/misc/oqo-wmi.c new file +10 -0
+/*
+ * Generic Device (interface-independent)
+ */
+
+static void oqo_kine_poll(struct input_polled_dev *dev)
+{
+	s16 x, y, z;
+	u16 lumin;
+	int good;
+	/*      struct timeval tv1, tv2; */

?

Quoted 81 lines
drivers/misc/oqo-wmi.c new file +81 -0
+	if (dev != oqo_kine_polled)
+		return;
+	if (orig.kine_itvl < 0)
+		return;
+
+	x = y = z = 0;
+	oqo_read_kine(&good, &x, &y, &z, &lumin);
+}
+
+static int __devinit oqo_kine_init(void)
+{
+	int err;
+
+	oqo_kine = input_allocate_device();
+	if (!oqo_kine)
+		return -ENOMEM;
+
+	oqo_kine->name = "OQO embedded accelerometer";
+	oqo_kine->phys = "platform:oqo-wmi:kine";
+	oqo_kine->id.bustype = 0;
+	oqo_kine->id.vendor = 0;
+	oqo_kine->id.product = 2;
+	oqo_kine->id.version = 0;
+	oqo_kine->evbit[0] = BIT_MASK(EV_ABS);
+	set_bit(ABS_X, oqo_kine->absbit);
+	set_bit(ABS_Y, oqo_kine->absbit);
+	set_bit(ABS_Z, oqo_kine->absbit);
+	set_bit(ABS_MISC, oqo_kine->absbit);
+	oqo_kine->absmin[ABS_X] =
+	    oqo_kine->absmin[ABS_Y] =
+	    oqo_kine->absmin[ABS_Z] = oqo_kine->absmin[ABS_MISC] = -32768;
+	oqo_kine->absmax[ABS_X] =
+	    oqo_kine->absmax[ABS_Y] =
+	    oqo_kine->absmax[ABS_Z] = oqo_kine->absmax[ABS_MISC] = 32767;
+
+	memcpy(oqo_kine->dev.bus_id, "kine", 4);
+
+	oqo_kine_polled = input_allocate_polled_device();
+	if (!oqo_kine_polled) {
+		err = -ENOMEM;
+		goto bail0;
+	}
+
+	oqo_kine_polled->poll = oqo_kine_poll;
+	oqo_kine_polled->poll_interval = 250;
+	oqo_kine_polled->input = oqo_kine;
+
+	orig.kine_itvl = -1;	/* prevent callback from running */
+	err = input_register_polled_device(oqo_kine_polled);
+	if (err) {
+		printk(OQO_ERR "Failed to register OQO kine input\n");
+		goto bail1;
+	}
+
+	/* This will allow the callback to run now if successful. */
+	orig.kine_itvl = smread_u8(OQO_O2_SMB0_ACCEL_POLL_ITVL);
+	smwrite_u8(OQO_O2_SMB0_ACCEL_POLL_ITVL, 250);
+	curr.kine_itvl = smread_u8(OQO_O2_SMB0_ACCEL_POLL_ITVL);
+	if (orig.kine_itvl < 0 || curr.kine_itvl != 250) {
+		printk(OQO_ERR "Test communication with kine sensor failed\n");
+		err = -ENODEV;
+		goto bail2;
+	}
+
+	printk(OQO_INFO "Created OQO kine input.\n");
+	printk(OQO_INFO "Firmware interval %ims, driver interval %ims\n",
+	       curr.kine_itvl, oqo_kine_polled->poll_interval);
+	return 0;
+bail2:
+	input_unregister_polled_device(oqo_kine_polled);
+bail1:
+	input_free_polled_device(oqo_kine_polled);	/* frees oqo_kine */
+	return err;
+bail0:
+	input_free_device(oqo_kine);
+	return err;
+}
+
+static void __devexit oqo_kine_fini(void)
+{
+	smwrite_u8(OQO_O2_SMB0_ACCEL_POLL_ITVL, orig.kine_itvl);

drivers/misc/oqo-wmi.c line 650

Quoted 10 lines
drivers/misc/oqo-wmi.c new file +10 -0
+	input_unregister_polled_device(oqo_kine_polled);
+	input_free_polled_device(oqo_kine_polled);
+}
+
+/*
+ * Backlight device
+ */
+static int read_brightness(struct backlight_device *bd)
+{
+	return (int)smread_s16(OQO_O2_SMB0_BL_HI, OQO_O2_SMB0_BL_LO);

smread_s16() already returns int.

drivers/misc/oqo-wmi.c line 656

Quoted 6 lines
drivers/misc/oqo-wmi.c new file +6 -0
+}
+
+static int update_bl_status(struct backlight_device *bd)
+{
+	return smwrite_s16(OQO_O2_SMB0_BL_HI,
+			   OQO_O2_SMB0_BL_LO, (s16) bd->props.brightness);

This driver does quite a lot of casting of things which the compiler will happily do for us. This could be viewed as having documentation benefit, but not much, and it is atypical.

Quoted 5 lines
drivers/misc/oqo-wmi.c new file +2 -0
+}
+
 
...
#7 lore raw

Yeah Matthew picked up the code before I did a final cleanup, at that
point I was still just waiting for someone(anyone) to test it. So the edges are
rough and there's still some painter's tape stuck to the walls. I guess I can afford some time to shave the fuzzies off, but Matthew and I should coordinate timewise if this all needs to be fixed before it's in a repository -- my opportunities to code are sparse, and his attention is probably a highly contested resource.

Thanks for the review (and Sven in other reply, too) -- a few items I probably would have missed cleaning. Some stuff is the wmi core and sample drivers at least as they were when I coded, and those aren't from me.

Quoted 5 lines · Andrew Morton
Quoted 5 lines
drivers/misc/oqo-wmi.c new file +5 -0
+static int smread_s16(u8 hi_addr, u8 lo_addr)
+{
+	s16 ret = -1;
+	acpi_status status;
+	u8 r;
Quoted 8 lines

drivers/misc/oqo-wmi.c line 344

Quoted 5 lines
drivers/misc/oqo-wmi.c new file +5 -0
+
+	/* Keep some ACPI routines off the SMBUS */
+	status = oqo_lock_smbus(1);
+	if (ACPI_FAILURE(status))
+		goto skip;

Does this mean that we didn't take the lock? If so, will running oqo_lock_smbus(0) be correct?

That does this (in DSL) after unpacking arguments and running a switch statement:

Store (BUF1, VFLG)
Return (0x00)

...VFLG being a preallocated RAM value in the ACPI VM. That's all. Famous last words but I really don't see it failing ever, or if it does the ACPI VM's in deep doodoo anyways. At any rate it will not spin.

Really an ACPI doctor is needed to even explain why it's there at all -- there's a real mutex on the SMBUS, and this here is not it.

It seems to just be some sort of badly coded (in DSDT) advisory that keeps the machine awake. I only even bothered to call it because it seemed like safer to do so than not. We should really be the only writer, as ACPI routines merely read it (and only a few of them.) The only access to it is through the WMI interface. Can we just claim exclusivity on using that given this is an omnibus driver?

Quoted 3 lines

This driver does quite a lot of casting of things which the compiler will happily do for us. This could be viewed as having documentation benefit, but not much, and it is atypical.

Yep I know, I'm cast-and-paren trigger happy. Keeps me sane, and has saved my butt a few times, but can be cleaned to match style.

P.S. I think it was Sven that pointed out a

label:
return

IIRC that was due to a cranky GCC warning FWIW.

--
Brian

reviewer
#8 lore raw
In reply to Matthew Garrett

Matthew,
this refreshed patch sits in my test tree from about a month ago. I guess i was expecting you to reply to the three comments on it before pushing it upstream.

thanks,
Len Brown, Intel Open Source Technology Center

#9 lore raw
Quoted 4 lines · Len Brown

Matthew,
this refreshed patch sits in my test tree from about a month ago. I guess i was expecting you to reply to the three comments on it before pushing it upstream.

Ah, sorry, I didn't realise you'd picked it up. I'm still trying to find some time to spend with the hardware to make sure a cleaned up version works correctly.

Signature · 1 line
Matthew Garrett | [email protected]
reviewer
#10 lore raw
Quoted 9 lines
Quoted 5 lines

Matthew,
this refreshed patch sits in my test tree from about a month ago. I guess i was expecting you to reply to the three comments on it before pushing it upstream.

Ah, sorry, I didn't realise you'd picked it up. I'm still trying to find some time to spend with the hardware to make sure a cleaned up version works correctly.

No sweat, just wanted you to know that v1 is in acpi test and thus will be in linux-next because of it.
It will patiently wait there until you're ready to refresh it.

cheers,
-Len

reviewer
#11 lore raw

In reply to Len Brown

From: Matthew Garrett <[email protected]>

Matthew,
Here is the version in my tree -- refreshed to live in drivers/platform/x86/.

cheers,
-Len

oqo-wmi provides a WMI-based interface to backlight and rfkill control on model 2 OQO devices. It was originally written by Brian Julin (<[email protected]>) - I've ported it to the rfkill layer and tidied it up a little.

---
 MAINTAINERS                    |    5 +
 drivers/platform/x86/Kconfig   |   13 +
 drivers/platform/x86/Makefile  |    1 +
 drivers/platform/x86/oqo-wmi.c |  941 ++++++++++++++++++++++++++++++++++++++++
 4 files changed, 960 insertions(+), 0 deletions(-)
 create mode 100644 drivers/platform/x86/oqo-wmi.c
MAINTAINERS +5 -0
index 57e0309..fef4adb 100644
@@ -3239,6 +3239,11 @@ M:	[email protected]
 L:	[email protected]
 S:	Maintained
 
+OQO WMI EXTRAS DRIVER
+P:	Matthew Garrett
+M:	[email protected]
+S:	Maintained
+
 ORACLE CLUSTER FILESYSTEM 2 (OCFS2)
 P:	Mark Fasheh
 M:	[email protected]
drivers/platform/x86/Kconfig +13 -0
index 9e8f948..017fb81 100644
@@ -132,6 +132,19 @@ config MSI_LAPTOP
 
 	  If you have an MSI S270 laptop, say Y or M here.
 
+config OQO_WMI
+       tristate "OQO WMI extras"
+       depends on ACPI_WMI
+       depends on INPUT
+       depends on RFKILL
+       depends on BACKLIGHT_CLASS_DEVICE
+       help
+         Say Y here if you want to support rfkill and backlight control on
+         series 2 OQO handheld devices.
+
+         To compile this driver as a module, choose M here: the module will
+         be called oqo-wmi.
+
 config PANASONIC_LAPTOP
 	tristate "Panasonic Laptop Extras"
 	depends on INPUT && ACPI
drivers/platform/x86/Makefile +1 -0
index e290651..37ac07f 100644
@@ -13,6 +13,7 @@ obj-$(CONFIG_TC1100_WMI)	+= tc1100-wmi.o
 obj-$(CONFIG_SONY_LAPTOP)	+= sony-laptop.o
 obj-$(CONFIG_THINKPAD_ACPI)	+= thinkpad_acpi.o
 obj-$(CONFIG_FUJITSU_LAPTOP)	+= fujitsu-laptop.o
+obj-$(CONFIG_OQO_WMI)		+= oqo-wmi.o
 obj-$(CONFIG_PANASONIC_LAPTOP)	+= panasonic-laptop.o
 obj-$(CONFIG_INTEL_MENLOW)	+= intel_menlow.o
 obj-$(CONFIG_ACPI_WMI)		+= wmi.o
drivers/platform/x86/oqo-wmi.c new file +941 -0

941 lines not on this page · raw message

Signature · 1 line
1.6.1.76.gc123b