-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
There exist tilt switches that simply report their tilt-state via some gpios. The number and orientation of their axes can vary depending on the switch used and the build of the device. Also two or more one-axis switches could be combined to provide multi-dimensional orientation. One example of a device using such a switch is the family of Qisda ebook readers, where the switch provides information about the landscape / portrait orientation of the device. The example in Documentation/input/gpio-tilt.txt documents exactly this one-axis device. Signed-off-by: Heiko Stuebner <heiko@sntech.de> Signed-off-by: Dmitry Torokhov <dtor@mail.ru>
- Loading branch information
Heiko Stübner
authored and
Dmitry Torokhov
committed
Dec 1, 2011
1 parent
a6c6178
commit 3bfd5c5
Showing
5 changed files
with
404 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,103 @@ | ||
Driver for tilt-switches connected via GPIOs | ||
============================================ | ||
|
||
Generic driver to read data from tilt switches connected via gpios. | ||
Orientation can be provided by one or more than one tilt switches, | ||
i.e. each tilt switch providing one axis, and the number of axes | ||
is also not limited. | ||
|
||
|
||
Data structures: | ||
---------------- | ||
|
||
The array of struct gpio in the gpios field is used to list the gpios | ||
that represent the current tilt state. | ||
|
||
The array of struct gpio_tilt_axis describes the axes that are reported | ||
to the input system. The values set therein are used for the | ||
input_set_abs_params calls needed to init the axes. | ||
|
||
The array of struct gpio_tilt_state maps gpio states to the corresponding | ||
values to report. The gpio state is represented as a bitfield where the | ||
bit-index corresponds to the index of the gpio in the struct gpio array. | ||
In the same manner the values stored in the axes array correspond to | ||
the elements of the gpio_tilt_axis-array. | ||
|
||
|
||
Example: | ||
-------- | ||
|
||
Example configuration for a single TS1003 tilt switch that rotates around | ||
one axis in 4 steps and emitts the current tilt via two GPIOs. | ||
|
||
static int sg060_tilt_enable(struct device *dev) { | ||
/* code to enable the sensors */ | ||
}; | ||
|
||
static void sg060_tilt_disable(struct device *dev) { | ||
/* code to disable the sensors */ | ||
}; | ||
|
||
static struct gpio sg060_tilt_gpios[] = { | ||
{ SG060_TILT_GPIO_SENSOR1, GPIOF_IN, "tilt_sensor1" }, | ||
{ SG060_TILT_GPIO_SENSOR2, GPIOF_IN, "tilt_sensor2" }, | ||
}; | ||
|
||
static struct gpio_tilt_state sg060_tilt_states[] = { | ||
{ | ||
.gpios = (0 << 1) | (0 << 0), | ||
.axes = (int[]) { | ||
0, | ||
}, | ||
}, { | ||
.gpios = (0 << 1) | (1 << 0), | ||
.axes = (int[]) { | ||
1, /* 90 degrees */ | ||
}, | ||
}, { | ||
.gpios = (1 << 1) | (1 << 0), | ||
.axes = (int[]) { | ||
2, /* 180 degrees */ | ||
}, | ||
}, { | ||
.gpios = (1 << 1) | (0 << 0), | ||
.axes = (int[]) { | ||
3, /* 270 degrees */ | ||
}, | ||
}, | ||
}; | ||
|
||
static struct gpio_tilt_axis sg060_tilt_axes[] = { | ||
{ | ||
.axis = ABS_RY, | ||
.min = 0, | ||
.max = 3, | ||
.fuzz = 0, | ||
.flat = 0, | ||
}, | ||
}; | ||
|
||
static struct gpio_tilt_platform_data sg060_tilt_pdata= { | ||
.gpios = sg060_tilt_gpios, | ||
.nr_gpios = ARRAY_SIZE(sg060_tilt_gpios), | ||
|
||
.axes = sg060_tilt_axes, | ||
.nr_axes = ARRAY_SIZE(sg060_tilt_axes), | ||
|
||
.states = sg060_tilt_states, | ||
.nr_states = ARRAY_SIZE(sg060_tilt_states), | ||
|
||
.debounce_interval = 100, | ||
|
||
.poll_interval = 1000, | ||
.enable = sg060_tilt_enable, | ||
.disable = sg060_tilt_disable, | ||
}; | ||
|
||
static struct platform_device sg060_device_tilt = { | ||
.name = "gpio-tilt-polled", | ||
.id = -1, | ||
.dev = { | ||
.platform_data = &sg060_tilt_pdata, | ||
}, | ||
}; |
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,213 @@ | ||
/* | ||
* Driver for tilt switches connected via GPIO lines | ||
* not capable of generating interrupts | ||
* | ||
* Copyright (C) 2011 Heiko Stuebner <heiko@sntech.de> | ||
* | ||
* based on: drivers/input/keyboard/gpio_keys_polled.c | ||
* | ||
* Copyright (C) 2007-2010 Gabor Juhos <juhosg@openwrt.org> | ||
* Copyright (C) 2010 Nuno Goncalves <nunojpg@gmail.com> | ||
* | ||
* This program is free software; you can redistribute it and/or modify | ||
* it under the terms of the GNU General Public License version 2 as | ||
* published by the Free Software Foundation. | ||
*/ | ||
|
||
#include <linux/kernel.h> | ||
#include <linux/module.h> | ||
#include <linux/init.h> | ||
#include <linux/slab.h> | ||
#include <linux/input.h> | ||
#include <linux/input-polldev.h> | ||
#include <linux/ioport.h> | ||
#include <linux/platform_device.h> | ||
#include <linux/gpio.h> | ||
#include <linux/input/gpio_tilt.h> | ||
|
||
#define DRV_NAME "gpio-tilt-polled" | ||
|
||
struct gpio_tilt_polled_dev { | ||
struct input_polled_dev *poll_dev; | ||
struct device *dev; | ||
const struct gpio_tilt_platform_data *pdata; | ||
|
||
int last_state; | ||
|
||
int threshold; | ||
int count; | ||
}; | ||
|
||
static void gpio_tilt_polled_poll(struct input_polled_dev *dev) | ||
{ | ||
struct gpio_tilt_polled_dev *tdev = dev->private; | ||
const struct gpio_tilt_platform_data *pdata = tdev->pdata; | ||
struct input_dev *input = dev->input; | ||
struct gpio_tilt_state *tilt_state = NULL; | ||
int state, i; | ||
|
||
if (tdev->count < tdev->threshold) { | ||
tdev->count++; | ||
} else { | ||
state = 0; | ||
for (i = 0; i < pdata->nr_gpios; i++) | ||
state |= (!!gpio_get_value(pdata->gpios[i].gpio) << i); | ||
|
||
if (state != tdev->last_state) { | ||
for (i = 0; i < pdata->nr_states; i++) | ||
if (pdata->states[i].gpios == state) | ||
tilt_state = &pdata->states[i]; | ||
|
||
if (tilt_state) { | ||
for (i = 0; i < pdata->nr_axes; i++) | ||
input_report_abs(input, | ||
pdata->axes[i].axis, | ||
tilt_state->axes[i]); | ||
|
||
input_sync(input); | ||
} | ||
|
||
tdev->count = 0; | ||
tdev->last_state = state; | ||
} | ||
} | ||
} | ||
|
||
static void gpio_tilt_polled_open(struct input_polled_dev *dev) | ||
{ | ||
struct gpio_tilt_polled_dev *tdev = dev->private; | ||
const struct gpio_tilt_platform_data *pdata = tdev->pdata; | ||
|
||
if (pdata->enable) | ||
pdata->enable(tdev->dev); | ||
|
||
/* report initial state of the axes */ | ||
tdev->last_state = -1; | ||
tdev->count = tdev->threshold; | ||
gpio_tilt_polled_poll(tdev->poll_dev); | ||
} | ||
|
||
static void gpio_tilt_polled_close(struct input_polled_dev *dev) | ||
{ | ||
struct gpio_tilt_polled_dev *tdev = dev->private; | ||
const struct gpio_tilt_platform_data *pdata = tdev->pdata; | ||
|
||
if (pdata->disable) | ||
pdata->disable(tdev->dev); | ||
} | ||
|
||
static int __devinit gpio_tilt_polled_probe(struct platform_device *pdev) | ||
{ | ||
const struct gpio_tilt_platform_data *pdata = pdev->dev.platform_data; | ||
struct device *dev = &pdev->dev; | ||
struct gpio_tilt_polled_dev *tdev; | ||
struct input_polled_dev *poll_dev; | ||
struct input_dev *input; | ||
int error, i; | ||
|
||
if (!pdata || !pdata->poll_interval) | ||
return -EINVAL; | ||
|
||
tdev = kzalloc(sizeof(struct gpio_tilt_polled_dev), GFP_KERNEL); | ||
if (!tdev) { | ||
dev_err(dev, "no memory for private data\n"); | ||
return -ENOMEM; | ||
} | ||
|
||
error = gpio_request_array(pdata->gpios, pdata->nr_gpios); | ||
if (error) { | ||
dev_err(dev, | ||
"Could not request tilt GPIOs: %d\n", error); | ||
goto err_free_tdev; | ||
} | ||
|
||
poll_dev = input_allocate_polled_device(); | ||
if (!poll_dev) { | ||
dev_err(dev, "no memory for polled device\n"); | ||
error = -ENOMEM; | ||
goto err_free_gpios; | ||
} | ||
|
||
poll_dev->private = tdev; | ||
poll_dev->poll = gpio_tilt_polled_poll; | ||
poll_dev->poll_interval = pdata->poll_interval; | ||
poll_dev->open = gpio_tilt_polled_open; | ||
poll_dev->close = gpio_tilt_polled_close; | ||
|
||
input = poll_dev->input; | ||
|
||
input->name = pdev->name; | ||
input->phys = DRV_NAME"/input0"; | ||
input->dev.parent = &pdev->dev; | ||
|
||
input->id.bustype = BUS_HOST; | ||
input->id.vendor = 0x0001; | ||
input->id.product = 0x0001; | ||
input->id.version = 0x0100; | ||
|
||
__set_bit(EV_ABS, input->evbit); | ||
for (i = 0; i < pdata->nr_axes; i++) | ||
input_set_abs_params(input, pdata->axes[i].axis, | ||
pdata->axes[i].min, pdata->axes[i].max, | ||
pdata->axes[i].fuzz, pdata->axes[i].flat); | ||
|
||
tdev->threshold = DIV_ROUND_UP(pdata->debounce_interval, | ||
pdata->poll_interval); | ||
|
||
tdev->poll_dev = poll_dev; | ||
tdev->dev = dev; | ||
tdev->pdata = pdata; | ||
|
||
error = input_register_polled_device(poll_dev); | ||
if (error) { | ||
dev_err(dev, "unable to register polled device, err=%d\n", | ||
error); | ||
goto err_free_polldev; | ||
} | ||
|
||
platform_set_drvdata(pdev, tdev); | ||
|
||
return 0; | ||
|
||
err_free_polldev: | ||
input_free_polled_device(poll_dev); | ||
err_free_gpios: | ||
gpio_free_array(pdata->gpios, pdata->nr_gpios); | ||
err_free_tdev: | ||
kfree(tdev); | ||
|
||
return error; | ||
} | ||
|
||
static int __devexit gpio_tilt_polled_remove(struct platform_device *pdev) | ||
{ | ||
struct gpio_tilt_polled_dev *tdev = platform_get_drvdata(pdev); | ||
const struct gpio_tilt_platform_data *pdata = tdev->pdata; | ||
|
||
platform_set_drvdata(pdev, NULL); | ||
|
||
input_unregister_polled_device(tdev->poll_dev); | ||
input_free_polled_device(tdev->poll_dev); | ||
|
||
gpio_free_array(pdata->gpios, pdata->nr_gpios); | ||
|
||
kfree(tdev); | ||
|
||
return 0; | ||
} | ||
|
||
static struct platform_driver gpio_tilt_polled_driver = { | ||
.probe = gpio_tilt_polled_probe, | ||
.remove = __devexit_p(gpio_tilt_polled_remove), | ||
.driver = { | ||
.name = DRV_NAME, | ||
.owner = THIS_MODULE, | ||
}, | ||
}; | ||
|
||
module_platform_driver(gpio_tilt_polled_driver); | ||
|
||
MODULE_LICENSE("GPL v2"); | ||
MODULE_AUTHOR("Heiko Stuebner <heiko@sntech.de>"); | ||
MODULE_DESCRIPTION("Polled GPIO tilt driver"); | ||
MODULE_ALIAS("platform:" DRV_NAME); |
Oops, something went wrong.