Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

[PATCH] classmate-laptop: Add support for Classmate V3 accelerometer.

13 views
Skip to first unread message

Miguel Gómez

unread,
Jun 29, 2012, 8:50:01 AM6/29/12
to
Classmate V3 laptop includes a new accelerometer that can't be handled by
previous driver. This patch adds a new driver to handle it.

Signed-off-by: Miguel Gómez <mag...@igalia.com>
---
drivers/platform/x86/classmate-laptop.c | 400 ++++++++++++++++++++++++++++++-
1 file changed, 398 insertions(+), 2 deletions(-)

diff --git a/drivers/platform/x86/classmate-laptop.c b/drivers/platform/x86/classmate-laptop.c
index 94f93b6..f8cd3ad 100644
--- a/drivers/platform/x86/classmate-laptop.c
+++ b/drivers/platform/x86/classmate-laptop.c
@@ -31,12 +31,18 @@ MODULE_LICENSE("GPL");

struct cmpc_accel {
int sensitivity;
+ int g_select;
+ int inputdev_state;
};

-#define CMPC_ACCEL_SENSITIVITY_DEFAULT 5
+#define CMPC_ACCEL_DEV_STATE_CLOSED 0
+#define CMPC_ACCEL_DEV_STATE_OPEN 1

+#define CMPC_ACCEL_SENSITIVITY_DEFAULT 5
+#define CMPC_ACCEL_G_SELECT_DEFAULT 0

#define CMPC_ACCEL_HID "ACCE0000"
+#define CMPC_ACCEL_HID_V3 "ACCE0001"
#define CMPC_TABLET_HID "TBLT0000"
#define CMPC_IPML_HID "IPML200"
#define CMPC_KEYS_HID "FnBT0000"
@@ -76,7 +82,388 @@ static int cmpc_remove_acpi_notify_device(struct acpi_device *acpi)
}

/*
- * Accelerometer code.
+ * Accelerometer code for Classmate V3
+ */
+static acpi_status cmpc_start_accel_v3(acpi_handle handle)
+{
+ union acpi_object param[4];
+ struct acpi_object_list input;
+ acpi_status status;
+
+ param[0].type = ACPI_TYPE_INTEGER;
+ param[0].integer.value = 0x3;
+ param[1].type = ACPI_TYPE_INTEGER;
+ param[1].integer.value = 0;
+ param[2].type = ACPI_TYPE_INTEGER;
+ param[2].integer.value = 0;
+ param[3].type = ACPI_TYPE_INTEGER;
+ param[3].integer.value = 0;
+ input.count = 4;
+ input.pointer = param;
+ status = acpi_evaluate_object(handle, "ACMD", &input, NULL);
+ return status;
+}
+
+static acpi_status cmpc_stop_accel_v3(acpi_handle handle)
+{
+ union acpi_object param[4];
+ struct acpi_object_list input;
+ acpi_status status;
+
+ param[0].type = ACPI_TYPE_INTEGER;
+ param[0].integer.value = 0x4;
+ param[1].type = ACPI_TYPE_INTEGER;
+ param[1].integer.value = 0;
+ param[2].type = ACPI_TYPE_INTEGER;
+ param[2].integer.value = 0;
+ param[3].type = ACPI_TYPE_INTEGER;
+ param[3].integer.value = 0;
+ input.count = 4;
+ input.pointer = param;
+ status = acpi_evaluate_object(handle, "ACMD", &input, NULL);
+ return status;
+}
+
+static acpi_status cmpc_accel_set_sensitivity_v3(acpi_handle handle, int val)
+{
+ union acpi_object param[4];
+ struct acpi_object_list input;
+
+ param[0].type = ACPI_TYPE_INTEGER;
+ param[0].integer.value = 0x02;
+ param[1].type = ACPI_TYPE_INTEGER;
+ param[1].integer.value = val;
+ param[2].type = ACPI_TYPE_INTEGER;
+ param[2].integer.value = 0;
+ param[3].type = ACPI_TYPE_INTEGER;
+ param[3].integer.value = 0;
+ input.count = 4;
+ input.pointer = param;
+ return acpi_evaluate_object(handle, "ACMD", &input, NULL);
+}
+
+static acpi_status cmpc_accel_set_g_select_v3(acpi_handle handle, int val)
+{
+ union acpi_object param[4];
+ struct acpi_object_list input;
+
+ param[0].type = ACPI_TYPE_INTEGER;
+ param[0].integer.value = 0x05;
+ param[1].type = ACPI_TYPE_INTEGER;
+ param[1].integer.value = val;
+ param[2].type = ACPI_TYPE_INTEGER;
+ param[2].integer.value = 0;
+ param[3].type = ACPI_TYPE_INTEGER;
+ param[3].integer.value = 0;
+ input.count = 4;
+ input.pointer = param;
+ return acpi_evaluate_object(handle, "ACMD", &input, NULL);
+}
+
+static acpi_status cmpc_get_accel_v3(acpi_handle handle,
+ int16_t *x,
+ int16_t *y,
+ int16_t *z)
+{
+ union acpi_object param[4];
+ struct acpi_object_list input;
+ struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
+ int16_t *locs;
+ acpi_status status;
+
+ param[0].type = ACPI_TYPE_INTEGER;
+ param[0].integer.value = 0x01;
+ param[1].type = ACPI_TYPE_INTEGER;
+ param[1].integer.value = 0;
+ param[2].type = ACPI_TYPE_INTEGER;
+ param[2].integer.value = 0;
+ param[3].type = ACPI_TYPE_INTEGER;
+ param[3].integer.value = 0;
+ input.count = 4;
+ input.pointer = param;
+ status = acpi_evaluate_object(handle, "ACMD", &input, &output);
+ if (ACPI_SUCCESS(status)) {
+ union acpi_object *obj;
+ obj = output.pointer;
+ locs = (int16_t *) obj->buffer.pointer;
+ *x = locs[0];
+ *y = locs[1];
+ *z = locs[2];
+ kfree(output.pointer);
+ }
+ return status;
+}
+
+static void cmpc_accel_handler_v3(struct acpi_device *dev, u32 event)
+{
+ if (event == 0x81) {
+ int16_t x, y, z;
+ acpi_status status;
+
+ status = cmpc_get_accel_v3(dev->handle, &x, &y, &z);
+ if (ACPI_SUCCESS(status)) {
+ struct input_dev *inputdev = dev_get_drvdata(&dev->dev);
+
+ input_report_abs(inputdev, ABS_X, x);
+ input_report_abs(inputdev, ABS_Y, y);
+ input_report_abs(inputdev, ABS_Z, z);
+ input_sync(inputdev);
+ }
+ }
+}
+
+static ssize_t cmpc_accel_sensitivity_show_v3(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct acpi_device *acpi;
+ struct input_dev *inputdev;
+ struct cmpc_accel *accel;
+
+ acpi = to_acpi_device(dev);
+ inputdev = dev_get_drvdata(&acpi->dev);
+ accel = dev_get_drvdata(&inputdev->dev);
+
+ return sprintf(buf, "%d\n", accel->sensitivity);
+}
+
+static ssize_t cmpc_accel_sensitivity_store_v3(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct acpi_device *acpi;
+ struct input_dev *inputdev;
+ struct cmpc_accel *accel;
+ unsigned long sensitivity;
+ int r;
+
+ acpi = to_acpi_device(dev);
+ inputdev = dev_get_drvdata(&acpi->dev);
+ accel = dev_get_drvdata(&inputdev->dev);
+
+ r = kstrtoul(buf, 0, &sensitivity);
+ if (r)
+ return r;
+
+ /* sensitivity must be between 1 and 127 */
+ if (sensitivity < 1 || sensitivity > 127)
+ return -EINVAL;
+
+ accel->sensitivity = sensitivity;
+ cmpc_accel_set_sensitivity_v3(acpi->handle, sensitivity);
+
+ return strnlen(buf, count);
+}
+
+static struct device_attribute cmpc_accel_sensitivity_attr_v3 = {
+ .attr = { .name = "sensitivity", .mode = 0660 },
+ .show = cmpc_accel_sensitivity_show_v3,
+ .store = cmpc_accel_sensitivity_store_v3
+};
+
+static ssize_t cmpc_accel_g_select_show_v3(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct acpi_device *acpi;
+ struct input_dev *inputdev;
+ struct cmpc_accel *accel;
+
+ acpi = to_acpi_device(dev);
+ inputdev = dev_get_drvdata(&acpi->dev);
+ accel = dev_get_drvdata(&inputdev->dev);
+
+ return sprintf(buf, "%d\n", accel->g_select);
+}
+
+static ssize_t cmpc_accel_g_select_store_v3(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct acpi_device *acpi;
+ struct input_dev *inputdev;
+ struct cmpc_accel *accel;
+ unsigned long g_select;
+ int r;
+
+ acpi = to_acpi_device(dev);
+ inputdev = dev_get_drvdata(&acpi->dev);
+ accel = dev_get_drvdata(&inputdev->dev);
+
+ r = kstrtoul(buf, 0, &g_select);
+ if (r)
+ return r;
+
+ /* 0 means 1.5g, 1 means 6g, everything else is wrong */
+ if (g_select != 0 && g_select != 1)
+ return -EINVAL;
+
+ accel->g_select = g_select;
+ cmpc_accel_set_g_select_v3(acpi->handle, g_select);
+
+ return strnlen(buf, count);
+}
+
+static struct device_attribute cmpc_accel_g_select_attr_v3 = {
+ .attr = { .name = "g_select", .mode = 0660 },
+ .show = cmpc_accel_g_select_show_v3,
+ .store = cmpc_accel_g_select_store_v3
+};
+
+static int cmpc_accel_open_v3(struct input_dev *input)
+{
+ struct acpi_device *acpi;
+ struct cmpc_accel *accel;
+
+ acpi = to_acpi_device(input->dev.parent);
+ accel = dev_get_drvdata(&input->dev);
+
+ cmpc_accel_set_sensitivity_v3(acpi->handle, accel->sensitivity);
+ cmpc_accel_set_g_select_v3(acpi->handle, accel->g_select);
+
+ if (ACPI_SUCCESS(cmpc_start_accel_v3(acpi->handle))) {
+ accel->inputdev_state = CMPC_ACCEL_DEV_STATE_OPEN;
+ return 0;
+ }
+ return -EIO;
+}
+
+static void cmpc_accel_close_v3(struct input_dev *input)
+{
+ struct acpi_device *acpi;
+ struct cmpc_accel *accel;
+
+ acpi = to_acpi_device(input->dev.parent);
+ accel = dev_get_drvdata(&input->dev);
+
+ cmpc_stop_accel_v3(acpi->handle);
+ accel->inputdev_state = CMPC_ACCEL_DEV_STATE_CLOSED;
+}
+
+static void cmpc_accel_idev_init_v3(struct input_dev *inputdev)
+{
+ set_bit(EV_ABS, inputdev->evbit);
+ input_set_abs_params(inputdev, ABS_X, -255, 255, 16, 0);
+ input_set_abs_params(inputdev, ABS_Y, -255, 255, 16, 0);
+ input_set_abs_params(inputdev, ABS_Z, -255, 255, 16, 0);
+ inputdev->open = cmpc_accel_open_v3;
+ inputdev->close = cmpc_accel_close_v3;
+}
+
+static int cmpc_accel_suspend_v3(struct acpi_device *acpi, pm_message_t state)
+{
+ struct input_dev *inputdev;
+ struct cmpc_accel *accel;
+
+ inputdev = dev_get_drvdata(&acpi->dev);
+ accel = dev_get_drvdata(&inputdev->dev);
+
+ if (accel->inputdev_state == CMPC_ACCEL_DEV_STATE_OPEN)
+ return cmpc_stop_accel_v3(acpi->handle);
+
+ return 0;
+}
+
+static int cmpc_accel_resume_v3(struct acpi_device *acpi)
+{
+
+ struct input_dev *inputdev;
+ struct cmpc_accel *accel;
+
+ inputdev = dev_get_drvdata(&acpi->dev);
+ accel = dev_get_drvdata(&inputdev->dev);
+
+ if (accel->inputdev_state == CMPC_ACCEL_DEV_STATE_OPEN) {
+ cmpc_accel_set_sensitivity_v3(acpi->handle, accel->sensitivity);
+ cmpc_accel_set_g_select_v3(acpi->handle, accel->g_select);
+
+ if (ACPI_FAILURE(cmpc_start_accel_v3(acpi->handle)))
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static int cmpc_accel_add_v3(struct acpi_device *acpi)
+{
+ int error;
+ struct input_dev *inputdev;
+ struct cmpc_accel *accel;
+
+ accel = kmalloc(sizeof(*accel), GFP_KERNEL);
+ if (!accel)
+ return -ENOMEM;
+
+ accel->inputdev_state = CMPC_ACCEL_DEV_STATE_CLOSED;
+
+ accel->sensitivity = CMPC_ACCEL_SENSITIVITY_DEFAULT;
+ cmpc_accel_set_sensitivity_v3(acpi->handle, accel->sensitivity);
+
+ error = device_create_file(&acpi->dev, &cmpc_accel_sensitivity_attr_v3);
+ if (error)
+ goto failed_sensitivity;
+
+ accel->g_select = CMPC_ACCEL_G_SELECT_DEFAULT;
+ cmpc_accel_set_g_select_v3(acpi->handle, accel->g_select);
+
+ error = device_create_file(&acpi->dev, &cmpc_accel_g_select_attr_v3);
+ if (error)
+ goto failed_g_select;
+
+ error = cmpc_add_acpi_notify_device(acpi, "cmpc_accel_v3",
+ cmpc_accel_idev_init_v3);
+ if (error)
+ goto failed_input;
+
+ inputdev = dev_get_drvdata(&acpi->dev);
+ dev_set_drvdata(&inputdev->dev, accel);
+
+ return 0;
+
+failed_input:
+ device_remove_file(&acpi->dev, &cmpc_accel_g_select_attr_v3);
+failed_g_select:
+ device_remove_file(&acpi->dev, &cmpc_accel_sensitivity_attr_v3);
+failed_sensitivity:
+ kfree(accel);
+ return error;
+}
+
+static int cmpc_accel_remove_v3(struct acpi_device *acpi, int type)
+{
+ struct input_dev *inputdev;
+ struct cmpc_accel *accel;
+
+ inputdev = dev_get_drvdata(&acpi->dev);
+ accel = dev_get_drvdata(&inputdev->dev);
+
+ device_remove_file(&acpi->dev, &cmpc_accel_sensitivity_attr_v3);
+ device_remove_file(&acpi->dev, &cmpc_accel_g_select_attr_v3);
+ return cmpc_remove_acpi_notify_device(acpi);
+}
+
+static const struct acpi_device_id cmpc_accel_device_ids_v3[] = {
+ {CMPC_ACCEL_HID_V3, 0},
+ {"", 0}
+};
+
+static struct acpi_driver cmpc_accel_acpi_driver_v3 = {
+ .owner = THIS_MODULE,
+ .name = "cmpc_accel_v3",
+ .class = "cmpc_accel_v3",
+ .ids = cmpc_accel_device_ids_v3,
+ .ops = {
+ .add = cmpc_accel_add_v3,
+ .remove = cmpc_accel_remove_v3,
+ .notify = cmpc_accel_handler_v3,
+ .suspend = cmpc_accel_suspend_v3,
+ .resume = cmpc_accel_resume_v3,
+ }
+};
+
+
+/*
+ * Accelerometer code for classmate V1 and V2
*/
static acpi_status cmpc_start_accel(acpi_handle handle)
{
@@ -723,8 +1110,15 @@ static int cmpc_init(void)
if (r)
goto failed_accel;

+ r = acpi_bus_register_driver(&cmpc_accel_acpi_driver_v3);
+ if (r)
+ goto failed_accel_v3;
+
return r;

+failed_accel_v3:
+ acpi_bus_unregister_driver(&cmpc_accel_acpi_driver);
+
failed_accel:
acpi_bus_unregister_driver(&cmpc_tablet_acpi_driver);

@@ -740,6 +1134,7 @@ failed_keys:

static void cmpc_exit(void)
{
+ acpi_bus_unregister_driver(&cmpc_accel_acpi_driver_v3);
acpi_bus_unregister_driver(&cmpc_accel_acpi_driver);
acpi_bus_unregister_driver(&cmpc_tablet_acpi_driver);
acpi_bus_unregister_driver(&cmpc_ipml_acpi_driver);
@@ -751,6 +1146,7 @@ module_exit(cmpc_exit);

static const struct acpi_device_id cmpc_device_ids[] = {
{CMPC_ACCEL_HID, 0},
+ {CMPC_ACCEL_HID_V3, 0},
{CMPC_TABLET_HID, 0},
{CMPC_IPML_HID, 0},
{CMPC_KEYS_HID, 0},
--
1.7.9.5

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

Miguel Gómez

unread,
Jun 29, 2012, 8:50:02 AM6/29/12
to
Classmate V3 has a different accelerometer than the previous versions. Main
differences between the new (ACCE0001) and the old one (ACCE0000) are:

* ACPI method calls need to receive 4 parameters instead of 2
* Values returned by the accelerometer are 2 bytes signed integers, instead
of one unsigned byte
* New accelerometer has a new attribute (g_select) besides the sensitivity one.
This attribute's possible values are 0 (meaning 1.5g) and 1 (meaning 6g).

I haven't been able to find the datasheet of the accelerometer, so I got most
of the behaviour of the device by looking at the previous model driver and by
experimenting with the hardware. Due to this, the range of the axis values
(currently defined as -255-255) may not be exact, and also the fuzz value (16)
may not be the best. But, with this configuration, the programs handling the
screen rotation developed for previous versions of Classmate are working
properly.

Initially I thought of modifying the ACCE0000 driver to be able to handle both
devices, but the patch was quite big and messy, and it might add problems to the
previous hardware, so I decided to implement it as a new driver, following the
structure of the ACCE0000 driver. This way the new code won't break the old one.

In order to differenciate the new driver code from the old one, all the
function names that belong to the new driver end with "v3".

Hope everything is ok :)

Regards!

Miguel Gómez (1):
classmate-laptop: Add support for accelerometer in classmate V3.

drivers/platform/x86/classmate-laptop.c | 400 ++++++++++++++++++++++++++++++-
1 file changed, 398 insertions(+), 2 deletions(-)

Miguel Gómez

unread,
Jun 29, 2012, 9:50:02 AM6/29/12
to
Classmate V4 laptop includes a new accelerometer that can't be handled by
previous driver. This patch adds a new driver to handle it.

Signed-off-by: Miguel Gómez <mag...@igalia.com>
---
drivers/platform/x86/classmate-laptop.c | 400 ++++++++++++++++++++++++++++++-
1 file changed, 398 insertions(+), 2 deletions(-)

diff --git a/drivers/platform/x86/classmate-laptop.c b/drivers/platform/x86/classmate-laptop.c
index 94f93b6..5de01bc 100644
--- a/drivers/platform/x86/classmate-laptop.c
+++ b/drivers/platform/x86/classmate-laptop.c
@@ -31,12 +31,18 @@ MODULE_LICENSE("GPL");

struct cmpc_accel {
int sensitivity;
+ int g_select;
+ int inputdev_state;
};

-#define CMPC_ACCEL_SENSITIVITY_DEFAULT 5
+#define CMPC_ACCEL_DEV_STATE_CLOSED 0
+#define CMPC_ACCEL_DEV_STATE_OPEN 1

+#define CMPC_ACCEL_SENSITIVITY_DEFAULT 5
+#define CMPC_ACCEL_G_SELECT_DEFAULT 0

#define CMPC_ACCEL_HID "ACCE0000"
+#define CMPC_ACCEL_HID_V4 "ACCE0001"
#define CMPC_TABLET_HID "TBLT0000"
#define CMPC_IPML_HID "IPML200"
#define CMPC_KEYS_HID "FnBT0000"
@@ -76,7 +82,388 @@ static int cmpc_remove_acpi_notify_device(struct acpi_device *acpi)
}

/*
- * Accelerometer code.
+ * Accelerometer code for Classmate V4
+ */
+static acpi_status cmpc_start_accel_v4(acpi_handle handle)
+{
+ union acpi_object param[4];
+ struct acpi_object_list input;
+ acpi_status status;
+
+ param[0].type = ACPI_TYPE_INTEGER;
+ param[0].integer.value = 0x3;
+ param[1].type = ACPI_TYPE_INTEGER;
+ param[1].integer.value = 0;
+ param[2].type = ACPI_TYPE_INTEGER;
+ param[2].integer.value = 0;
+ param[3].type = ACPI_TYPE_INTEGER;
+ param[3].integer.value = 0;
+ input.count = 4;
+ input.pointer = param;
+ status = acpi_evaluate_object(handle, "ACMD", &input, NULL);
+ return status;
+}
+
+static acpi_status cmpc_stop_accel_v4(acpi_handle handle)
+{
+ union acpi_object param[4];
+ struct acpi_object_list input;
+ acpi_status status;
+
+ param[0].type = ACPI_TYPE_INTEGER;
+ param[0].integer.value = 0x4;
+ param[1].type = ACPI_TYPE_INTEGER;
+ param[1].integer.value = 0;
+ param[2].type = ACPI_TYPE_INTEGER;
+ param[2].integer.value = 0;
+ param[3].type = ACPI_TYPE_INTEGER;
+ param[3].integer.value = 0;
+ input.count = 4;
+ input.pointer = param;
+ status = acpi_evaluate_object(handle, "ACMD", &input, NULL);
+ return status;
+}
+
+static acpi_status cmpc_accel_set_sensitivity_v4(acpi_handle handle, int val)
+{
+ union acpi_object param[4];
+ struct acpi_object_list input;
+
+ param[0].type = ACPI_TYPE_INTEGER;
+ param[0].integer.value = 0x02;
+ param[1].type = ACPI_TYPE_INTEGER;
+ param[1].integer.value = val;
+ param[2].type = ACPI_TYPE_INTEGER;
+ param[2].integer.value = 0;
+ param[3].type = ACPI_TYPE_INTEGER;
+ param[3].integer.value = 0;
+ input.count = 4;
+ input.pointer = param;
+ return acpi_evaluate_object(handle, "ACMD", &input, NULL);
+}
+
+static acpi_status cmpc_accel_set_g_select_v4(acpi_handle handle, int val)
+{
+ union acpi_object param[4];
+ struct acpi_object_list input;
+
+ param[0].type = ACPI_TYPE_INTEGER;
+ param[0].integer.value = 0x05;
+ param[1].type = ACPI_TYPE_INTEGER;
+ param[1].integer.value = val;
+ param[2].type = ACPI_TYPE_INTEGER;
+ param[2].integer.value = 0;
+ param[3].type = ACPI_TYPE_INTEGER;
+ param[3].integer.value = 0;
+ input.count = 4;
+ input.pointer = param;
+ return acpi_evaluate_object(handle, "ACMD", &input, NULL);
+}
+
+static acpi_status cmpc_get_accel_v4(acpi_handle handle,
+static void cmpc_accel_handler_v4(struct acpi_device *dev, u32 event)
+{
+ if (event == 0x81) {
+ int16_t x, y, z;
+ acpi_status status;
+
+ status = cmpc_get_accel_v4(dev->handle, &x, &y, &z);
+ if (ACPI_SUCCESS(status)) {
+ struct input_dev *inputdev = dev_get_drvdata(&dev->dev);
+
+ input_report_abs(inputdev, ABS_X, x);
+ input_report_abs(inputdev, ABS_Y, y);
+ input_report_abs(inputdev, ABS_Z, z);
+ input_sync(inputdev);
+ }
+ }
+}
+
+static ssize_t cmpc_accel_sensitivity_show_v4(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct acpi_device *acpi;
+ struct input_dev *inputdev;
+ struct cmpc_accel *accel;
+
+ acpi = to_acpi_device(dev);
+ inputdev = dev_get_drvdata(&acpi->dev);
+ accel = dev_get_drvdata(&inputdev->dev);
+
+ return sprintf(buf, "%d\n", accel->sensitivity);
+}
+
+static ssize_t cmpc_accel_sensitivity_store_v4(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct acpi_device *acpi;
+ struct input_dev *inputdev;
+ struct cmpc_accel *accel;
+ unsigned long sensitivity;
+ int r;
+
+ acpi = to_acpi_device(dev);
+ inputdev = dev_get_drvdata(&acpi->dev);
+ accel = dev_get_drvdata(&inputdev->dev);
+
+ r = kstrtoul(buf, 0, &sensitivity);
+ if (r)
+ return r;
+
+ /* sensitivity must be between 1 and 127 */
+ if (sensitivity < 1 || sensitivity > 127)
+ return -EINVAL;
+
+ accel->sensitivity = sensitivity;
+ cmpc_accel_set_sensitivity_v4(acpi->handle, sensitivity);
+
+ return strnlen(buf, count);
+}
+
+static struct device_attribute cmpc_accel_sensitivity_attr_v4 = {
+ .attr = { .name = "sensitivity", .mode = 0660 },
+ .show = cmpc_accel_sensitivity_show_v4,
+ .store = cmpc_accel_sensitivity_store_v4
+};
+
+static ssize_t cmpc_accel_g_select_show_v4(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct acpi_device *acpi;
+ struct input_dev *inputdev;
+ struct cmpc_accel *accel;
+
+ acpi = to_acpi_device(dev);
+ inputdev = dev_get_drvdata(&acpi->dev);
+ accel = dev_get_drvdata(&inputdev->dev);
+
+ return sprintf(buf, "%d\n", accel->g_select);
+}
+
+static ssize_t cmpc_accel_g_select_store_v4(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct acpi_device *acpi;
+ struct input_dev *inputdev;
+ struct cmpc_accel *accel;
+ unsigned long g_select;
+ int r;
+
+ acpi = to_acpi_device(dev);
+ inputdev = dev_get_drvdata(&acpi->dev);
+ accel = dev_get_drvdata(&inputdev->dev);
+
+ r = kstrtoul(buf, 0, &g_select);
+ if (r)
+ return r;
+
+ /* 0 means 1.5g, 1 means 6g, everything else is wrong */
+ if (g_select != 0 && g_select != 1)
+ return -EINVAL;
+
+ accel->g_select = g_select;
+ cmpc_accel_set_g_select_v4(acpi->handle, g_select);
+
+ return strnlen(buf, count);
+}
+
+static struct device_attribute cmpc_accel_g_select_attr_v4 = {
+ .attr = { .name = "g_select", .mode = 0660 },
+ .show = cmpc_accel_g_select_show_v4,
+ .store = cmpc_accel_g_select_store_v4
+};
+
+static int cmpc_accel_open_v4(struct input_dev *input)
+{
+ struct acpi_device *acpi;
+ struct cmpc_accel *accel;
+
+ acpi = to_acpi_device(input->dev.parent);
+ accel = dev_get_drvdata(&input->dev);
+
+ cmpc_accel_set_sensitivity_v4(acpi->handle, accel->sensitivity);
+ cmpc_accel_set_g_select_v4(acpi->handle, accel->g_select);
+
+ if (ACPI_SUCCESS(cmpc_start_accel_v4(acpi->handle))) {
+ accel->inputdev_state = CMPC_ACCEL_DEV_STATE_OPEN;
+ return 0;
+ }
+ return -EIO;
+}
+
+static void cmpc_accel_close_v4(struct input_dev *input)
+{
+ struct acpi_device *acpi;
+ struct cmpc_accel *accel;
+
+ acpi = to_acpi_device(input->dev.parent);
+ accel = dev_get_drvdata(&input->dev);
+
+ cmpc_stop_accel_v4(acpi->handle);
+ accel->inputdev_state = CMPC_ACCEL_DEV_STATE_CLOSED;
+}
+
+static void cmpc_accel_idev_init_v4(struct input_dev *inputdev)
+{
+ set_bit(EV_ABS, inputdev->evbit);
+ input_set_abs_params(inputdev, ABS_X, -255, 255, 16, 0);
+ input_set_abs_params(inputdev, ABS_Y, -255, 255, 16, 0);
+ input_set_abs_params(inputdev, ABS_Z, -255, 255, 16, 0);
+ inputdev->open = cmpc_accel_open_v4;
+ inputdev->close = cmpc_accel_close_v4;
+}
+
+static int cmpc_accel_suspend_v4(struct acpi_device *acpi, pm_message_t state)
+{
+ struct input_dev *inputdev;
+ struct cmpc_accel *accel;
+
+ inputdev = dev_get_drvdata(&acpi->dev);
+ accel = dev_get_drvdata(&inputdev->dev);
+
+ if (accel->inputdev_state == CMPC_ACCEL_DEV_STATE_OPEN)
+ return cmpc_stop_accel_v4(acpi->handle);
+
+ return 0;
+}
+
+static int cmpc_accel_resume_v4(struct acpi_device *acpi)
+{
+
+ struct input_dev *inputdev;
+ struct cmpc_accel *accel;
+
+ inputdev = dev_get_drvdata(&acpi->dev);
+ accel = dev_get_drvdata(&inputdev->dev);
+
+ if (accel->inputdev_state == CMPC_ACCEL_DEV_STATE_OPEN) {
+ cmpc_accel_set_sensitivity_v4(acpi->handle, accel->sensitivity);
+ cmpc_accel_set_g_select_v4(acpi->handle, accel->g_select);
+
+ if (ACPI_FAILURE(cmpc_start_accel_v4(acpi->handle)))
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static int cmpc_accel_add_v4(struct acpi_device *acpi)
+{
+ int error;
+ struct input_dev *inputdev;
+ struct cmpc_accel *accel;
+
+ accel = kmalloc(sizeof(*accel), GFP_KERNEL);
+ if (!accel)
+ return -ENOMEM;
+
+ accel->inputdev_state = CMPC_ACCEL_DEV_STATE_CLOSED;
+
+ accel->sensitivity = CMPC_ACCEL_SENSITIVITY_DEFAULT;
+ cmpc_accel_set_sensitivity_v4(acpi->handle, accel->sensitivity);
+
+ error = device_create_file(&acpi->dev, &cmpc_accel_sensitivity_attr_v4);
+ if (error)
+ goto failed_sensitivity;
+
+ accel->g_select = CMPC_ACCEL_G_SELECT_DEFAULT;
+ cmpc_accel_set_g_select_v4(acpi->handle, accel->g_select);
+
+ error = device_create_file(&acpi->dev, &cmpc_accel_g_select_attr_v4);
+ if (error)
+ goto failed_g_select;
+
+ error = cmpc_add_acpi_notify_device(acpi, "cmpc_accel_v4",
+ cmpc_accel_idev_init_v4);
+ if (error)
+ goto failed_input;
+
+ inputdev = dev_get_drvdata(&acpi->dev);
+ dev_set_drvdata(&inputdev->dev, accel);
+
+ return 0;
+
+failed_input:
+ device_remove_file(&acpi->dev, &cmpc_accel_g_select_attr_v4);
+failed_g_select:
+ device_remove_file(&acpi->dev, &cmpc_accel_sensitivity_attr_v4);
+failed_sensitivity:
+ kfree(accel);
+ return error;
+}
+
+static int cmpc_accel_remove_v4(struct acpi_device *acpi, int type)
+{
+ struct input_dev *inputdev;
+ struct cmpc_accel *accel;
+
+ inputdev = dev_get_drvdata(&acpi->dev);
+ accel = dev_get_drvdata(&inputdev->dev);
+
+ device_remove_file(&acpi->dev, &cmpc_accel_sensitivity_attr_v4);
+ device_remove_file(&acpi->dev, &cmpc_accel_g_select_attr_v4);
+ return cmpc_remove_acpi_notify_device(acpi);
+}
+
+static const struct acpi_device_id cmpc_accel_device_ids_v4[] = {
+ {CMPC_ACCEL_HID_V4, 0},
+ {"", 0}
+};
+
+static struct acpi_driver cmpc_accel_acpi_driver_v4 = {
+ .owner = THIS_MODULE,
+ .name = "cmpc_accel_v4",
+ .class = "cmpc_accel_v4",
+ .ids = cmpc_accel_device_ids_v4,
+ .ops = {
+ .add = cmpc_accel_add_v4,
+ .remove = cmpc_accel_remove_v4,
+ .notify = cmpc_accel_handler_v4,
+ .suspend = cmpc_accel_suspend_v4,
+ .resume = cmpc_accel_resume_v4,
+ }
+};
+
+
+/*
+ * Accelerometer code for Classmate versions prior to V4
*/
static acpi_status cmpc_start_accel(acpi_handle handle)
{
@@ -723,8 +1110,15 @@ static int cmpc_init(void)
if (r)
goto failed_accel;

+ r = acpi_bus_register_driver(&cmpc_accel_acpi_driver_v4);
+ if (r)
+ goto failed_accel_v4;
+
return r;

+failed_accel_v4:
+ acpi_bus_unregister_driver(&cmpc_accel_acpi_driver);
+
failed_accel:
acpi_bus_unregister_driver(&cmpc_tablet_acpi_driver);

@@ -740,6 +1134,7 @@ failed_keys:

static void cmpc_exit(void)
{
+ acpi_bus_unregister_driver(&cmpc_accel_acpi_driver_v4);
acpi_bus_unregister_driver(&cmpc_accel_acpi_driver);
acpi_bus_unregister_driver(&cmpc_tablet_acpi_driver);
acpi_bus_unregister_driver(&cmpc_ipml_acpi_driver);
@@ -751,6 +1146,7 @@ module_exit(cmpc_exit);

static const struct acpi_device_id cmpc_device_ids[] = {
{CMPC_ACCEL_HID, 0},
+ {CMPC_ACCEL_HID_V4, 0},
{CMPC_TABLET_HID, 0},
{CMPC_IPML_HID, 0},
{CMPC_KEYS_HID, 0},

Miguel Gómez

unread,
Jun 29, 2012, 9:50:02 AM6/29/12
to
************************************************************************

Sorry, I made a mistake with the version. The patch is for Classmate V4
and not for V3, so I adjusted it accordingly.

************************************************************************

Classmate V4 has a different accelerometer than the previous versions. Main
differences between the new (ACCE0001) and the old one (ACCE0000) are:

* ACPI method calls need to receive 4 parameters instead of 2
* Values returned by the accelerometer are 2 bytes signed integers, instead
of one unsigned byte
* New accelerometer has a new attribute (g_select) besides the sensitivity one.
This attribute's possible values are 0 (meaning 1.5g) and 1 (meaning 6g).

I haven't been able to find the datasheet of the accelerometer, so I got most
of the behaviour of the device by looking at the previous model driver and by
experimenting with the hardware. Due to this, the range of the axis values
(currently defined as -255-255) may not be exact, and also the fuzz value (16)
may not be the best. But, with this configuration, the programs handling the
screen rotation developed for previous versions of Classmate are working
properly.

Initially I thought of modifying the ACCE0000 driver to be able to handle both
devices, but the patch was quite big and messy, and it might add problems to the
previous hardware, so I decided to implement it as a new driver, following the
structure of the ACCE0000 driver. This way the new code won't break the old one.

In order to differenciate the new driver code from the old one, all the
function names that belong to the new driver end with "v4".

Hope everything is ok :)

Regards!

Miguel Gómez (1):
classmate-laptop: Add support for Classmate V4 accelerometer.

Miguel Gómez

unread,
Jul 17, 2012, 10:40:01 AM7/17/12
to
Hi!

> Miguel Gómez (1):
> classmate-laptop: Add support for Classmate V4 accelerometer.
>
> drivers/platform/x86/classmate-laptop.c | 400 ++++++++++++++++++++++++++++++-
> 1 file changed, 398 insertions(+), 2 deletions(-)

Anyone had a moment to give a look to this code? I'm eager to get some
feedback and fix whatever is needed to put it in shape... and start
playing Tux Racer with the Classmate 4 accelerometer :)

Regards!!

--
Miguel Gómez
Igalia - http://www.igalia.com

Thadeu Lima de Souza Cascardo

unread,
Jul 17, 2012, 6:50:01 PM7/17/12
to
On Fri, Jun 29, 2012 at 03:39:48PM +0200, Miguel Gómez wrote:
> Classmate V4 laptop includes a new accelerometer that can't be handled by
> previous driver. This patch adds a new driver to handle it.
>
> Signed-off-by: Miguel Gómez <mag...@igalia.com>

Hi, Miguel.

This seems OK to me. Thanks for this work. Do you have the other
functionality of the driver working? I am interested to know if you have
the function buttons working.

Regards.
Cascardo.

Acked-by: Thadeu Lima de Souza Cascardo <casc...@holoscopio.com>
signature.asc

Miguel Gómez

unread,
Jul 18, 2012, 10:00:02 AM7/18/12
to
> On Fri, Jun 29, 2012 at 03:39:48PM +0200, Miguel Gómez wrote:
>> Classmate V4 laptop includes a new accelerometer that can't be handled by
>> previous driver. This patch adds a new driver to handle it.
>>
>> Signed-off-by: Miguel Gómez <mag...@igalia.com>
>
> Hi, Miguel.
>
> This seems OK to me. Thanks for this work. Do you have the other
> functionality of the driver working? I am interested to know if you have
> the function buttons working.

Hi Thadeu

I've been giving a look to the buttons code. Currently they are not
working because the hardware id reported by the device (FNBT0000) is not
the same as the one defined by the driver (FnBT0000), as you an one of
my colleagues commented in this thread:

https://lkml.org/lkml/2011/6/21/341

I've replaced the id in the driver to FNBT0000 and it made the buttons
work like a charm (enabling control of the backlight and LAN as well).
To ensure compatibility with older versions of the kernel, a new id can
be added (FNBT0000) besides the existent one (FnBT0000), and that works
as well (as proposed in the thread).

It seems that this problem got a bit forgotten in the lists. Quoting
yourself from the last email in the thread:

> I am copying the acpi list and Len Brown to see if they can tell us
> which one should be the right solution:
>
> 1) reverting the patch in acpica that puts all names in uppercase
> 2) using a case-insensitive match in acpi bus driver
> 3) using uppercase name in classmate-laptop driver

I think the easiest and less risky way to fix this is by adding a new
hardware id (as proposed in the thread's patch), as it doesn't add any
side effects (that the other solutions might add), and it doesn't break
the behaviour of the old code.

What do you think?

Regards!

--
Miguel Gómez
Igalia - http://www.igalia.com


Matthew Garrett

unread,
Jul 23, 2012, 9:30:02 AM7/23/12
to
On Wed, Jul 18, 2012 at 03:53:48PM +0200, Miguel G�mez wrote:

> >1) reverting the patch in acpica that puts all names in uppercase
> >2) using a case-insensitive match in acpi bus driver
> >3) using uppercase name in classmate-laptop driver
>
> I think the easiest and less risky way to fix this is by adding a
> new hardware id (as proposed in the thread's patch), as it doesn't
> add any side effects (that the other solutions might add), and it
> doesn't break the behaviour of the old code.

I've lost track of the implementation state here - if all names are
upper-cased in acpica, why is this not already matching?

--
Matthew Garrett | mj...@srcf.ucam.org

Miguel Gómez

unread,
Jul 23, 2012, 9:40:03 AM7/23/12
to
El 23/07/12 15:28, Matthew Garrett escribi�:
> On Wed, Jul 18, 2012 at 03:53:48PM +0200, Miguel G�mez wrote:
>
>>> 1) reverting the patch in acpica that puts all names in uppercase
>>> 2) using a case-insensitive match in acpi bus driver
>>> 3) using uppercase name in classmate-laptop driver
>>
>> I think the easiest and less risky way to fix this is by adding a
>> new hardware id (as proposed in the thread's patch), as it doesn't
>> add any side effects (that the other solutions might add), and it
>> doesn't break the behaviour of the old code.
>
> I've lost track of the implementation state here - if all names are
> upper-cased in acpica, why is this not already matching?

Names are upper-cased in acpica, so the device is reported as FNBT0000.
But in the driver it's named FnBT0000, and that's why it doesn't match.

--
Miguel G�mez
Igalia - http://www.igalia.com

Matthew Garrett

unread,
Jul 23, 2012, 9:40:04 AM7/23/12
to
On Mon, Jul 23, 2012 at 03:33:27PM +0200, Miguel G�mez wrote:

> Names are upper-cased in acpica, so the device is reported as
> FNBT0000. But in the driver it's named FnBT0000, and that's why it
> doesn't match.

So just change the existing entry in the driver to FNBT0000?

--
Matthew Garrett | mj...@srcf.ucam.org

Miguel Gómez

unread,
Jul 23, 2012, 9:50:03 AM7/23/12
to
El 23/07/12 15:36, Matthew Garrett escribi�:
> On Mon, Jul 23, 2012 at 03:33:27PM +0200, Miguel G�mez wrote:
>
>> Names are upper-cased in acpica, so the device is reported as
>> FNBT0000. But in the driver it's named FnBT0000, and that's why it
>> doesn't match.
>
> So just change the existing entry in the driver to FNBT0000?

I'd go for it. I can send a patch if you want. But not sure about
Thadeu's opinion. In the old thread it seems that he wanted to explore
other options besides that.

Do you agree with that change Thadeu?


--
Miguel G�mez
Igalia - http://www.igalia.com

Thadeu Lima de Souza Cascardo

unread,
Jul 23, 2012, 11:20:02 PM7/23/12
to
On Mon, Jul 23, 2012 at 03:44:41PM +0200, Miguel Gómez wrote:
> El 23/07/12 15:36, Matthew Garrett escribió:
> >On Mon, Jul 23, 2012 at 03:33:27PM +0200, Miguel Gómez wrote:
> >
> >>Names are upper-cased in acpica, so the device is reported as
> >>FNBT0000. But in the driver it's named FnBT0000, and that's why it
> >>doesn't match.
> >
> >So just change the existing entry in the driver to FNBT0000?
>
> I'd go for it. I can send a patch if you want. But not sure about
> Thadeu's opinion. In the old thread it seems that he wanted to
> explore other options besides that.
>
> Do you agree with that change Thadeu?
>

I'd say the other options were met with silence. So, go forward with the
patch and I'll ack it.

Thanks.
Cascardo.
signature.asc

Miguel Gómez

unread,
Jul 24, 2012, 9:10:02 AM7/24/12
to
Since ACPI devices ids were changed to use always upper-case letters, the ACPI
id of the extra keys (FNBT0000) was not maching the one defined in the driver
(FnBT0000), causing the extra keys not to work.

The patch replaces the driver id with the one reported by ACPI, fixing the
problem.

Signed-off-by: Miguel Gómez <mag...@igalia.com>
---
drivers/platform/x86/classmate-laptop.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/platform/x86/classmate-laptop.c b/drivers/platform/x86/classmate-laptop.c
index e2230a2..43e6e54 100644
--- a/drivers/platform/x86/classmate-laptop.c
+++ b/drivers/platform/x86/classmate-laptop.c
@@ -39,7 +39,7 @@ struct cmpc_accel {
#define CMPC_ACCEL_HID "ACCE0000"
#define CMPC_TABLET_HID "TBLT0000"
#define CMPC_IPML_HID "IPML200"
-#define CMPC_KEYS_HID "FnBT0000"
+#define CMPC_KEYS_HID "FNBT0000"

/*
* Generic input device code.
--
1.7.9.5

Miguel Gómez

unread,
Jul 24, 2012, 9:20:02 AM7/24/12
to
> Since ACPI devices ids were changed to use always upper-case letters, the ACPI
> id of the extra keys (FNBT0000) was not maching the one defined in the driver
> (FnBT0000), causing the extra keys not to work.
>
> The patch replaces the driver id with the one reported by ACPI, fixing the
> problem.
>
> Signed-off-by: Miguel Gómez <mag...@igalia.com>

I've created this patch on top of linux-next, but it won't apply over
the patch I've just sent for the accelerometer (neither the later over
the former).

How do you want me to proceed? Should I sent this again over the
accelerometer patch?

Regards!

--
Miguel Gómez
Igalia - http://www.igalia.com

Matthew Garrett

unread,
Jul 24, 2012, 9:30:02 AM7/24/12
to
On Tue, Jul 24, 2012 at 03:17:26PM +0200, Miguel G�mez wrote:
> I've created this patch on top of linux-next, but it won't apply
> over the patch I've just sent for the accelerometer (neither the
> later over the former).
>
> How do you want me to proceed? Should I sent this again over the
> accelerometer patch?

I can merge them. Thanks for the patch!

--
Matthew Garrett | mj...@srcf.ucam.org
0 new messages