diff options
author | Mike Lockwood <lockwood@android.com> | 2009-05-04 18:48:00 -0400 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2009-06-19 14:00:52 -0400 |
commit | 241e12879be74fdfadf3abc820a2e3c455599c3c (patch) | |
tree | 372fcd1160d940dae9487da97c8bee46da3791e3 | |
parent | 5d14a573a4da521d4ed7acd0c7d8a975887b2dd2 (diff) |
Staging: android: timed_gpio: Separate timed_output class into a separate driver.
Signed-off-by: Mike Lockwood <lockwood@android.com>
Signed-off-by: Arve Hjønnevåg <arve@android.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
-rw-r--r-- | drivers/staging/android/Kconfig | 6 | ||||
-rw-r--r-- | drivers/staging/android/Makefile | 1 | ||||
-rw-r--r-- | drivers/staging/android/timed_gpio.c | 98 | ||||
-rw-r--r-- | drivers/staging/android/timed_gpio.h | 4 | ||||
-rw-r--r-- | drivers/staging/android/timed_output.c | 121 | ||||
-rw-r--r-- | drivers/staging/android/timed_output.h | 37 |
6 files changed, 210 insertions, 57 deletions
diff --git a/drivers/staging/android/Kconfig b/drivers/staging/android/Kconfig index 604bd1e0d546..178450876314 100644 --- a/drivers/staging/android/Kconfig +++ b/drivers/staging/android/Kconfig | |||
@@ -73,9 +73,13 @@ config ANDROID_RAM_CONSOLE_EARLY_SIZE | |||
73 | default 0 | 73 | default 0 |
74 | depends on ANDROID_RAM_CONSOLE_EARLY_INIT | 74 | depends on ANDROID_RAM_CONSOLE_EARLY_INIT |
75 | 75 | ||
76 | config ANDROID_TIMED_OUTPUT | ||
77 | bool "Timed output class driver" | ||
78 | default y | ||
79 | |||
76 | config ANDROID_TIMED_GPIO | 80 | config ANDROID_TIMED_GPIO |
77 | tristate "Android timed gpio driver" | 81 | tristate "Android timed gpio driver" |
78 | depends on GENERIC_GPIO | 82 | depends on GENERIC_GPIO && ANDROID_TIMED_OUTPUT |
79 | default n | 83 | default n |
80 | 84 | ||
81 | config ANDROID_LOW_MEMORY_KILLER | 85 | config ANDROID_LOW_MEMORY_KILLER |
diff --git a/drivers/staging/android/Makefile b/drivers/staging/android/Makefile index 95209d6273a1..8e057e626d11 100644 --- a/drivers/staging/android/Makefile +++ b/drivers/staging/android/Makefile | |||
@@ -1,5 +1,6 @@ | |||
1 | obj-$(CONFIG_ANDROID_BINDER_IPC) += binder.o | 1 | obj-$(CONFIG_ANDROID_BINDER_IPC) += binder.o |
2 | obj-$(CONFIG_ANDROID_LOGGER) += logger.o | 2 | obj-$(CONFIG_ANDROID_LOGGER) += logger.o |
3 | obj-$(CONFIG_ANDROID_RAM_CONSOLE) += ram_console.o | 3 | obj-$(CONFIG_ANDROID_RAM_CONSOLE) += ram_console.o |
4 | obj-$(CONFIG_ANDROID_TIMED_OUTPUT) += timed_output.o | ||
4 | obj-$(CONFIG_ANDROID_TIMED_GPIO) += timed_gpio.o | 5 | obj-$(CONFIG_ANDROID_TIMED_GPIO) += timed_gpio.o |
5 | obj-$(CONFIG_ANDROID_LOW_MEMORY_KILLER) += lowmemorykiller.o | 6 | obj-$(CONFIG_ANDROID_LOW_MEMORY_KILLER) += lowmemorykiller.o |
diff --git a/drivers/staging/android/timed_gpio.c b/drivers/staging/android/timed_gpio.c index 33daff0481d2..be7cdaa783ae 100644 --- a/drivers/staging/android/timed_gpio.c +++ b/drivers/staging/android/timed_gpio.c | |||
@@ -20,13 +20,12 @@ | |||
20 | #include <linux/err.h> | 20 | #include <linux/err.h> |
21 | #include <linux/gpio.h> | 21 | #include <linux/gpio.h> |
22 | 22 | ||
23 | #include "timed_output.h" | ||
23 | #include "timed_gpio.h" | 24 | #include "timed_gpio.h" |
24 | 25 | ||
25 | 26 | ||
26 | static struct class *timed_gpio_class; | ||
27 | |||
28 | struct timed_gpio_data { | 27 | struct timed_gpio_data { |
29 | struct device *dev; | 28 | struct timed_output_dev dev; |
30 | struct hrtimer timer; | 29 | struct hrtimer timer; |
31 | spinlock_t lock; | 30 | spinlock_t lock; |
32 | unsigned gpio; | 31 | unsigned gpio; |
@@ -36,70 +35,62 @@ struct timed_gpio_data { | |||
36 | 35 | ||
37 | static enum hrtimer_restart gpio_timer_func(struct hrtimer *timer) | 36 | static enum hrtimer_restart gpio_timer_func(struct hrtimer *timer) |
38 | { | 37 | { |
39 | struct timed_gpio_data *gpio_data = container_of(timer, struct timed_gpio_data, timer); | 38 | struct timed_gpio_data *data = |
39 | container_of(timer, struct timed_gpio_data, timer); | ||
40 | 40 | ||
41 | gpio_direction_output(gpio_data->gpio, gpio_data->active_low ? 1 : 0); | 41 | gpio_direction_output(data->gpio, data->active_low ? 1 : 0); |
42 | return HRTIMER_NORESTART; | 42 | return HRTIMER_NORESTART; |
43 | } | 43 | } |
44 | 44 | ||
45 | static ssize_t gpio_enable_show(struct device *dev, struct device_attribute *attr, char *buf) | 45 | static int gpio_get_time(struct timed_output_dev *dev) |
46 | { | 46 | { |
47 | struct timed_gpio_data *gpio_data = dev_get_drvdata(dev); | 47 | struct timed_gpio_data *data = |
48 | int remaining; | 48 | container_of(dev, struct timed_gpio_data, dev); |
49 | 49 | ||
50 | if (hrtimer_active(&gpio_data->timer)) { | 50 | if (hrtimer_active(&data->timer)) { |
51 | ktime_t r = hrtimer_get_remaining(&gpio_data->timer); | 51 | ktime_t r = hrtimer_get_remaining(&data->timer); |
52 | struct timeval t = ktime_to_timeval(r); | 52 | struct timeval t = ktime_to_timeval(r); |
53 | remaining = t.tv_sec * 1000 + t.tv_usec / 1000; | 53 | return t.tv_sec * 1000 + t.tv_usec / 1000; |
54 | } else | 54 | } else |
55 | remaining = 0; | 55 | return 0; |
56 | |||
57 | return sprintf(buf, "%d\n", remaining); | ||
58 | } | 56 | } |
59 | 57 | ||
60 | static ssize_t gpio_enable_store( | 58 | static void gpio_enable(struct timed_output_dev *dev, int value) |
61 | struct device *dev, struct device_attribute *attr, | ||
62 | const char *buf, size_t size) | ||
63 | { | 59 | { |
64 | struct timed_gpio_data *gpio_data = dev_get_drvdata(dev); | 60 | struct timed_gpio_data *data = |
65 | int value; | 61 | container_of(dev, struct timed_gpio_data, dev); |
66 | unsigned long flags; | 62 | unsigned long flags; |
67 | 63 | ||
68 | sscanf(buf, "%d", &value); | 64 | spin_lock_irqsave(&data->lock, flags); |
69 | |||
70 | spin_lock_irqsave(&gpio_data->lock, flags); | ||
71 | 65 | ||
72 | /* cancel previous timer and set GPIO according to value */ | 66 | /* cancel previous timer and set GPIO according to value */ |
73 | hrtimer_cancel(&gpio_data->timer); | 67 | hrtimer_cancel(&data->timer); |
74 | gpio_direction_output(gpio_data->gpio, gpio_data->active_low ? !value : !!value); | 68 | gpio_direction_output(data->gpio, data->active_low ? !value : !!value); |
75 | 69 | ||
76 | if (value > 0) { | 70 | if (value > 0) { |
77 | if (value > gpio_data->max_timeout) | 71 | if (value > data->max_timeout) |
78 | value = gpio_data->max_timeout; | 72 | value = data->max_timeout; |
79 | 73 | ||
80 | hrtimer_start(&gpio_data->timer, | 74 | hrtimer_start(&data->timer, |
81 | ktime_set(value / 1000, (value % 1000) * 1000000), | 75 | ktime_set(value / 1000, (value % 1000) * 1000000), |
82 | HRTIMER_MODE_REL); | 76 | HRTIMER_MODE_REL); |
83 | } | 77 | } |
84 | 78 | ||
85 | spin_unlock_irqrestore(&gpio_data->lock, flags); | 79 | spin_unlock_irqrestore(&data->lock, flags); |
86 | |||
87 | return size; | ||
88 | } | 80 | } |
89 | 81 | ||
90 | static DEVICE_ATTR(enable, S_IRUGO | S_IWUSR, gpio_enable_show, gpio_enable_store); | ||
91 | |||
92 | static int timed_gpio_probe(struct platform_device *pdev) | 82 | static int timed_gpio_probe(struct platform_device *pdev) |
93 | { | 83 | { |
94 | struct timed_gpio_platform_data *pdata = pdev->dev.platform_data; | 84 | struct timed_gpio_platform_data *pdata = pdev->dev.platform_data; |
95 | struct timed_gpio *cur_gpio; | 85 | struct timed_gpio *cur_gpio; |
96 | struct timed_gpio_data *gpio_data, *gpio_dat; | 86 | struct timed_gpio_data *gpio_data, *gpio_dat; |
97 | int i, ret = 0; | 87 | int i, j, ret = 0; |
98 | 88 | ||
99 | if (!pdata) | 89 | if (!pdata) |
100 | return -EBUSY; | 90 | return -EBUSY; |
101 | 91 | ||
102 | gpio_data = kzalloc(sizeof(struct timed_gpio_data) * pdata->num_gpios, GFP_KERNEL); | 92 | gpio_data = kzalloc(sizeof(struct timed_gpio_data) * pdata->num_gpios, |
93 | GFP_KERNEL); | ||
103 | if (!gpio_data) | 94 | if (!gpio_data) |
104 | return -ENOMEM; | 95 | return -ENOMEM; |
105 | 96 | ||
@@ -107,23 +98,26 @@ static int timed_gpio_probe(struct platform_device *pdev) | |||
107 | cur_gpio = &pdata->gpios[i]; | 98 | cur_gpio = &pdata->gpios[i]; |
108 | gpio_dat = &gpio_data[i]; | 99 | gpio_dat = &gpio_data[i]; |
109 | 100 | ||
110 | hrtimer_init(&gpio_dat->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); | 101 | hrtimer_init(&gpio_dat->timer, CLOCK_MONOTONIC, |
102 | HRTIMER_MODE_REL); | ||
111 | gpio_dat->timer.function = gpio_timer_func; | 103 | gpio_dat->timer.function = gpio_timer_func; |
112 | spin_lock_init(&gpio_dat->lock); | 104 | spin_lock_init(&gpio_dat->lock); |
113 | 105 | ||
106 | gpio_dat->dev.name = cur_gpio->name; | ||
107 | gpio_dat->dev.get_time = gpio_get_time; | ||
108 | gpio_dat->dev.enable = gpio_enable; | ||
109 | ret = timed_output_dev_register(&gpio_dat->dev); | ||
110 | if (ret < 0) { | ||
111 | for (j = 0; j < i; j++) | ||
112 | timed_output_dev_unregister(&gpio_data[i].dev); | ||
113 | kfree(gpio_data); | ||
114 | return ret; | ||
115 | } | ||
116 | |||
114 | gpio_dat->gpio = cur_gpio->gpio; | 117 | gpio_dat->gpio = cur_gpio->gpio; |
115 | gpio_dat->max_timeout = cur_gpio->max_timeout; | 118 | gpio_dat->max_timeout = cur_gpio->max_timeout; |
116 | gpio_dat->active_low = cur_gpio->active_low; | 119 | gpio_dat->active_low = cur_gpio->active_low; |
117 | gpio_direction_output(gpio_dat->gpio, gpio_dat->active_low); | 120 | gpio_direction_output(gpio_dat->gpio, gpio_dat->active_low); |
118 | |||
119 | gpio_dat->dev = device_create(timed_gpio_class, &pdev->dev, 0, "%s", cur_gpio->name); | ||
120 | if (unlikely(IS_ERR(gpio_dat->dev))) | ||
121 | return PTR_ERR(gpio_dat->dev); | ||
122 | |||
123 | dev_set_drvdata(gpio_dat->dev, gpio_dat); | ||
124 | ret = device_create_file(gpio_dat->dev, &dev_attr_enable); | ||
125 | if (ret) | ||
126 | return ret; | ||
127 | } | 121 | } |
128 | 122 | ||
129 | platform_set_drvdata(pdev, gpio_data); | 123 | platform_set_drvdata(pdev, gpio_data); |
@@ -137,10 +131,8 @@ static int timed_gpio_remove(struct platform_device *pdev) | |||
137 | struct timed_gpio_data *gpio_data = platform_get_drvdata(pdev); | 131 | struct timed_gpio_data *gpio_data = platform_get_drvdata(pdev); |
138 | int i; | 132 | int i; |
139 | 133 | ||
140 | for (i = 0; i < pdata->num_gpios; i++) { | 134 | for (i = 0; i < pdata->num_gpios; i++) |
141 | device_remove_file(gpio_data[i].dev, &dev_attr_enable); | 135 | timed_output_dev_unregister(&gpio_data[i].dev); |
142 | device_unregister(gpio_data[i].dev); | ||
143 | } | ||
144 | 136 | ||
145 | kfree(gpio_data); | 137 | kfree(gpio_data); |
146 | 138 | ||
@@ -151,22 +143,18 @@ static struct platform_driver timed_gpio_driver = { | |||
151 | .probe = timed_gpio_probe, | 143 | .probe = timed_gpio_probe, |
152 | .remove = timed_gpio_remove, | 144 | .remove = timed_gpio_remove, |
153 | .driver = { | 145 | .driver = { |
154 | .name = "timed-gpio", | 146 | .name = TIMED_GPIO_NAME, |
155 | .owner = THIS_MODULE, | 147 | .owner = THIS_MODULE, |
156 | }, | 148 | }, |
157 | }; | 149 | }; |
158 | 150 | ||
159 | static int __init timed_gpio_init(void) | 151 | static int __init timed_gpio_init(void) |
160 | { | 152 | { |
161 | timed_gpio_class = class_create(THIS_MODULE, "timed_output"); | ||
162 | if (IS_ERR(timed_gpio_class)) | ||
163 | return PTR_ERR(timed_gpio_class); | ||
164 | return platform_driver_register(&timed_gpio_driver); | 153 | return platform_driver_register(&timed_gpio_driver); |
165 | } | 154 | } |
166 | 155 | ||
167 | static void __exit timed_gpio_exit(void) | 156 | static void __exit timed_gpio_exit(void) |
168 | { | 157 | { |
169 | class_destroy(timed_gpio_class); | ||
170 | platform_driver_unregister(&timed_gpio_driver); | 158 | platform_driver_unregister(&timed_gpio_driver); |
171 | } | 159 | } |
172 | 160 | ||
diff --git a/drivers/staging/android/timed_gpio.h b/drivers/staging/android/timed_gpio.h index 78449b2161cf..a0e15f8be3f7 100644 --- a/drivers/staging/android/timed_gpio.h +++ b/drivers/staging/android/timed_gpio.h | |||
@@ -16,10 +16,12 @@ | |||
16 | #ifndef _LINUX_TIMED_GPIO_H | 16 | #ifndef _LINUX_TIMED_GPIO_H |
17 | #define _LINUX_TIMED_GPIO_H | 17 | #define _LINUX_TIMED_GPIO_H |
18 | 18 | ||
19 | #define TIMED_GPIO_NAME "timed-gpio" | ||
20 | |||
19 | struct timed_gpio { | 21 | struct timed_gpio { |
20 | const char *name; | 22 | const char *name; |
21 | unsigned gpio; | 23 | unsigned gpio; |
22 | int max_timeout; | 24 | int max_timeout; |
23 | u8 active_low; | 25 | u8 active_low; |
24 | }; | 26 | }; |
25 | 27 | ||
diff --git a/drivers/staging/android/timed_output.c b/drivers/staging/android/timed_output.c new file mode 100644 index 000000000000..62e79180421b --- /dev/null +++ b/drivers/staging/android/timed_output.c | |||
@@ -0,0 +1,121 @@ | |||
1 | /* drivers/misc/timed_output.c | ||
2 | * | ||
3 | * Copyright (C) 2009 Google, Inc. | ||
4 | * Author: Mike Lockwood <lockwood@android.com> | ||
5 | * | ||
6 | * This software is licensed under the terms of the GNU General Public | ||
7 | * License version 2, as published by the Free Software Foundation, and | ||
8 | * may be copied, distributed, and modified under those terms. | ||
9 | * | ||
10 | * This program is distributed in the hope that it will be useful, | ||
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
13 | * GNU General Public License for more details. | ||
14 | * | ||
15 | */ | ||
16 | |||
17 | #include <linux/module.h> | ||
18 | #include <linux/types.h> | ||
19 | #include <linux/device.h> | ||
20 | #include <linux/fs.h> | ||
21 | #include <linux/err.h> | ||
22 | |||
23 | #include "timed_output.h" | ||
24 | |||
25 | static struct class *timed_output_class; | ||
26 | static atomic_t device_count; | ||
27 | |||
28 | static ssize_t enable_show(struct device *dev, struct device_attribute *attr, | ||
29 | char *buf) | ||
30 | { | ||
31 | struct timed_output_dev *tdev = dev_get_drvdata(dev); | ||
32 | int remaining = tdev->get_time(tdev); | ||
33 | |||
34 | return sprintf(buf, "%d\n", remaining); | ||
35 | } | ||
36 | |||
37 | static ssize_t enable_store( | ||
38 | struct device *dev, struct device_attribute *attr, | ||
39 | const char *buf, size_t size) | ||
40 | { | ||
41 | struct timed_output_dev *tdev = dev_get_drvdata(dev); | ||
42 | int value; | ||
43 | |||
44 | sscanf(buf, "%d", &value); | ||
45 | tdev->enable(tdev, value); | ||
46 | |||
47 | return size; | ||
48 | } | ||
49 | |||
50 | static DEVICE_ATTR(enable, S_IRUGO | S_IWUSR, enable_show, enable_store); | ||
51 | |||
52 | static int create_timed_output_class(void) | ||
53 | { | ||
54 | if (!timed_output_class) { | ||
55 | timed_output_class = class_create(THIS_MODULE, "timed_output"); | ||
56 | if (IS_ERR(timed_output_class)) | ||
57 | return PTR_ERR(timed_output_class); | ||
58 | atomic_set(&device_count, 0); | ||
59 | } | ||
60 | |||
61 | return 0; | ||
62 | } | ||
63 | |||
64 | int timed_output_dev_register(struct timed_output_dev *tdev) | ||
65 | { | ||
66 | int ret; | ||
67 | |||
68 | if (!tdev || !tdev->name || !tdev->enable || !tdev->get_time) | ||
69 | return -EINVAL; | ||
70 | |||
71 | ret = create_timed_output_class(); | ||
72 | if (ret < 0) | ||
73 | return ret; | ||
74 | |||
75 | tdev->index = atomic_inc_return(&device_count); | ||
76 | tdev->dev = device_create(timed_output_class, NULL, | ||
77 | MKDEV(0, tdev->index), NULL, tdev->name); | ||
78 | if (IS_ERR(tdev->dev)) | ||
79 | return PTR_ERR(tdev->dev); | ||
80 | |||
81 | ret = device_create_file(tdev->dev, &dev_attr_enable); | ||
82 | if (ret < 0) | ||
83 | goto err_create_file; | ||
84 | |||
85 | dev_set_drvdata(tdev->dev, tdev); | ||
86 | tdev->state = 0; | ||
87 | return 0; | ||
88 | |||
89 | err_create_file: | ||
90 | device_destroy(timed_output_class, MKDEV(0, tdev->index)); | ||
91 | printk(KERN_ERR "timed_output: Failed to register driver %s\n", | ||
92 | tdev->name); | ||
93 | |||
94 | return ret; | ||
95 | } | ||
96 | EXPORT_SYMBOL_GPL(timed_output_dev_register); | ||
97 | |||
98 | void timed_output_dev_unregister(struct timed_output_dev *tdev) | ||
99 | { | ||
100 | device_remove_file(tdev->dev, &dev_attr_enable); | ||
101 | device_destroy(timed_output_class, MKDEV(0, tdev->index)); | ||
102 | dev_set_drvdata(tdev->dev, NULL); | ||
103 | } | ||
104 | EXPORT_SYMBOL_GPL(timed_output_dev_unregister); | ||
105 | |||
106 | static int __init timed_output_init(void) | ||
107 | { | ||
108 | return create_timed_output_class(); | ||
109 | } | ||
110 | |||
111 | static void __exit timed_output_exit(void) | ||
112 | { | ||
113 | class_destroy(timed_output_class); | ||
114 | } | ||
115 | |||
116 | module_init(timed_output_init); | ||
117 | module_exit(timed_output_exit); | ||
118 | |||
119 | MODULE_AUTHOR("Mike Lockwood <lockwood@android.com>"); | ||
120 | MODULE_DESCRIPTION("timed output class driver"); | ||
121 | MODULE_LICENSE("GPL"); | ||
diff --git a/drivers/staging/android/timed_output.h b/drivers/staging/android/timed_output.h new file mode 100644 index 000000000000..ec907ab2ff54 --- /dev/null +++ b/drivers/staging/android/timed_output.h | |||
@@ -0,0 +1,37 @@ | |||
1 | /* include/linux/timed_output.h | ||
2 | * | ||
3 | * Copyright (C) 2008 Google, Inc. | ||
4 | * | ||
5 | * This software is licensed under the terms of the GNU General Public | ||
6 | * License version 2, as published by the Free Software Foundation, and | ||
7 | * may be copied, distributed, and modified under those terms. | ||
8 | * | ||
9 | * This program is distributed in the hope that it will be useful, | ||
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
12 | * GNU General Public License for more details. | ||
13 | * | ||
14 | */ | ||
15 | |||
16 | #ifndef _LINUX_TIMED_OUTPUT_H | ||
17 | #define _LINUX_TIMED_OUTPUT_H | ||
18 | |||
19 | struct timed_output_dev { | ||
20 | const char *name; | ||
21 | |||
22 | /* enable the output and set the timer */ | ||
23 | void (*enable)(struct timed_output_dev *sdev, int timeout); | ||
24 | |||
25 | /* returns the current number of milliseconds remaining on the timer */ | ||
26 | int (*get_time)(struct timed_output_dev *sdev); | ||
27 | |||
28 | /* private data */ | ||
29 | struct device *dev; | ||
30 | int index; | ||
31 | int state; | ||
32 | }; | ||
33 | |||
34 | extern int timed_output_dev_register(struct timed_output_dev *dev); | ||
35 | extern void timed_output_dev_unregister(struct timed_output_dev *dev); | ||
36 | |||
37 | #endif | ||