This repository has been archived by the owner on Aug 5, 2022. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 48
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
The OLPC XO-1.5 has an ebook switch, triggered when the laptop screen is rotated then folding down, converting the device into ebook form. This switch is exposed through ACPI. Add a driver that exposes it to userspace as an input device. Signed-off-by: Daniel Drake <dsd@laptop.org> Acked-by: Dmitry Torokhov <dtor@mail.ru> Signed-off-by: Matthew Garrett <mjg@redhat.com>
- Loading branch information
Paul Fox
authored and
Matthew Garrett
committed
Mar 28, 2011
1 parent
18bcd0c
commit 89ca117
Showing
3 changed files
with
191 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,181 @@ | ||
/* | ||
* OLPC XO-1.5 ebook switch driver | ||
* (based on generic ACPI button driver) | ||
* | ||
* Copyright (C) 2009 Paul Fox <pgf@laptop.org> | ||
* Copyright (C) 2010 One Laptop per Child | ||
* | ||
* 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. | ||
*/ | ||
|
||
#include <linux/kernel.h> | ||
#include <linux/module.h> | ||
#include <linux/init.h> | ||
#include <linux/types.h> | ||
#include <linux/input.h> | ||
#include <acpi/acpi_bus.h> | ||
#include <acpi/acpi_drivers.h> | ||
|
||
#define MODULE_NAME "xo15-ebook" | ||
#define PREFIX MODULE_NAME ": " | ||
|
||
#define XO15_EBOOK_CLASS MODULE_NAME | ||
#define XO15_EBOOK_TYPE_UNKNOWN 0x00 | ||
#define XO15_EBOOK_NOTIFY_STATUS 0x80 | ||
|
||
#define XO15_EBOOK_SUBCLASS "ebook" | ||
#define XO15_EBOOK_HID "XO15EBK" | ||
#define XO15_EBOOK_DEVICE_NAME "EBook Switch" | ||
|
||
ACPI_MODULE_NAME(MODULE_NAME); | ||
|
||
MODULE_DESCRIPTION("OLPC XO-1.5 ebook switch driver"); | ||
MODULE_LICENSE("GPL"); | ||
|
||
static const struct acpi_device_id ebook_device_ids[] = { | ||
{ XO15_EBOOK_HID, 0 }, | ||
{ "", 0 }, | ||
}; | ||
MODULE_DEVICE_TABLE(acpi, ebook_device_ids); | ||
|
||
struct ebook_switch { | ||
struct input_dev *input; | ||
char phys[32]; /* for input device */ | ||
}; | ||
|
||
static int ebook_send_state(struct acpi_device *device) | ||
{ | ||
struct ebook_switch *button = acpi_driver_data(device); | ||
unsigned long long state; | ||
acpi_status status; | ||
|
||
status = acpi_evaluate_integer(device->handle, "EBK", NULL, &state); | ||
if (ACPI_FAILURE(status)) | ||
return -EIO; | ||
|
||
/* input layer checks if event is redundant */ | ||
input_report_switch(button->input, SW_TABLET_MODE, !state); | ||
input_sync(button->input); | ||
return 0; | ||
} | ||
|
||
static void ebook_switch_notify(struct acpi_device *device, u32 event) | ||
{ | ||
switch (event) { | ||
case ACPI_FIXED_HARDWARE_EVENT: | ||
case XO15_EBOOK_NOTIFY_STATUS: | ||
ebook_send_state(device); | ||
break; | ||
default: | ||
ACPI_DEBUG_PRINT((ACPI_DB_INFO, | ||
"Unsupported event [0x%x]\n", event)); | ||
break; | ||
} | ||
} | ||
|
||
static int ebook_switch_resume(struct acpi_device *device) | ||
{ | ||
return ebook_send_state(device); | ||
} | ||
|
||
static int ebook_switch_add(struct acpi_device *device) | ||
{ | ||
struct ebook_switch *button; | ||
struct input_dev *input; | ||
const char *hid = acpi_device_hid(device); | ||
char *name, *class; | ||
int error; | ||
|
||
button = kzalloc(sizeof(struct ebook_switch), GFP_KERNEL); | ||
if (!button) | ||
return -ENOMEM; | ||
|
||
device->driver_data = button; | ||
|
||
button->input = input = input_allocate_device(); | ||
if (!input) { | ||
error = -ENOMEM; | ||
goto err_free_button; | ||
} | ||
|
||
name = acpi_device_name(device); | ||
class = acpi_device_class(device); | ||
|
||
if (strcmp(hid, XO15_EBOOK_HID)) { | ||
printk(KERN_ERR PREFIX "Unsupported hid [%s]\n", hid); | ||
error = -ENODEV; | ||
goto err_free_input; | ||
} | ||
|
||
strcpy(name, XO15_EBOOK_DEVICE_NAME); | ||
sprintf(class, "%s/%s", XO15_EBOOK_CLASS, XO15_EBOOK_SUBCLASS); | ||
|
||
snprintf(button->phys, sizeof(button->phys), "%s/button/input0", hid); | ||
|
||
input->name = name; | ||
input->phys = button->phys; | ||
input->id.bustype = BUS_HOST; | ||
input->dev.parent = &device->dev; | ||
|
||
input->evbit[0] = BIT_MASK(EV_SW); | ||
set_bit(SW_TABLET_MODE, input->swbit); | ||
|
||
error = input_register_device(input); | ||
if (error) | ||
goto err_free_input; | ||
|
||
ebook_send_state(device); | ||
|
||
if (device->wakeup.flags.valid) { | ||
/* Button's GPE is run-wake GPE */ | ||
acpi_enable_gpe(device->wakeup.gpe_device, | ||
device->wakeup.gpe_number); | ||
device->wakeup.run_wake_count++; | ||
device_set_wakeup_enable(&device->dev, true); | ||
} | ||
|
||
return 0; | ||
|
||
err_free_input: | ||
input_free_device(input); | ||
err_free_button: | ||
kfree(button); | ||
return error; | ||
} | ||
|
||
static int ebook_switch_remove(struct acpi_device *device, int type) | ||
{ | ||
struct ebook_switch *button = acpi_driver_data(device); | ||
|
||
input_unregister_device(button->input); | ||
kfree(button); | ||
return 0; | ||
} | ||
|
||
static struct acpi_driver xo15_ebook_driver = { | ||
.name = MODULE_NAME, | ||
.class = XO15_EBOOK_CLASS, | ||
.ids = ebook_device_ids, | ||
.ops = { | ||
.add = ebook_switch_add, | ||
.resume = ebook_switch_resume, | ||
.remove = ebook_switch_remove, | ||
.notify = ebook_switch_notify, | ||
}, | ||
}; | ||
|
||
static int __init xo15_ebook_init(void) | ||
{ | ||
return acpi_bus_register_driver(&xo15_ebook_driver); | ||
} | ||
|
||
static void __exit xo15_ebook_exit(void) | ||
{ | ||
acpi_bus_unregister_driver(&xo15_ebook_driver); | ||
} | ||
|
||
module_init(xo15_ebook_init); | ||
module_exit(xo15_ebook_exit); |