| 1 | From e4c9c5d7d6d5deb124083678fe5d839d3133f60a Mon Sep 17 00:00:00 2001 |
| 2 | From: Mike Lockwood <lockwood@android.com> |
| 3 | Date: Mon, 12 Jan 2009 13:25:05 -0500 |
| 4 | Subject: [PATCH 054/134] timed_gpio: Separate timed_output class into a separate driver. |
| 5 | MIME-Version: 1.0 |
| 6 | Content-Type: text/plain; charset=utf-8 |
| 7 | Content-Transfer-Encoding: 8bit |
| 8 | |
| 9 | Signed-off-by: Mike Lockwood <lockwood@android.com> |
| 10 | Signed-off-by: Arve Hjønnevåg <arve@android.com> |
| 11 | --- |
| 12 | drivers/staging/android/Kconfig | 6 ++- |
| 13 | drivers/staging/android/Makefile | 1 + |
| 14 | drivers/staging/android/timed_gpio.c | 98 +++++++++++-------------- |
| 15 | drivers/staging/android/timed_gpio.h | 4 +- |
| 16 | drivers/staging/android/timed_output.c | 121 ++++++++++++++++++++++++++++++++ |
| 17 | drivers/staging/android/timed_output.h | 37 ++++++++++ |
| 18 | 6 files changed, 210 insertions(+), 57 deletions(-) |
| 19 | create mode 100644 drivers/staging/android/timed_output.c |
| 20 | create mode 100644 drivers/staging/android/timed_output.h |
| 21 | |
| 22 | --- a/drivers/staging/android/Kconfig |
| 23 | +++ b/drivers/staging/android/Kconfig |
| 24 | @@ -73,9 +73,13 @@ config ANDROID_RAM_CONSOLE_EARLY_SIZE |
| 25 | default 0 |
| 26 | depends on ANDROID_RAM_CONSOLE_EARLY_INIT |
| 27 | |
| 28 | +config ANDROID_TIMED_OUTPUT |
| 29 | + bool "Timed output class driver" |
| 30 | + default y |
| 31 | + |
| 32 | config ANDROID_TIMED_GPIO |
| 33 | tristate "Android timed gpio driver" |
| 34 | - depends on GENERIC_GPIO |
| 35 | + depends on GENERIC_GPIO && ANDROID_TIMED_OUTPUT |
| 36 | default n |
| 37 | |
| 38 | config ANDROID_LOW_MEMORY_KILLER |
| 39 | --- a/drivers/staging/android/Makefile |
| 40 | +++ b/drivers/staging/android/Makefile |
| 41 | @@ -1,5 +1,6 @@ |
| 42 | obj-$(CONFIG_ANDROID_BINDER_IPC) += binder.o |
| 43 | obj-$(CONFIG_ANDROID_LOGGER) += logger.o |
| 44 | obj-$(CONFIG_ANDROID_RAM_CONSOLE) += ram_console.o |
| 45 | +obj-$(CONFIG_ANDROID_TIMED_OUTPUT) += timed_output.o |
| 46 | obj-$(CONFIG_ANDROID_TIMED_GPIO) += timed_gpio.o |
| 47 | obj-$(CONFIG_ANDROID_LOW_MEMORY_KILLER) += lowmemorykiller.o |
| 48 | --- a/drivers/staging/android/timed_gpio.c |
| 49 | +++ b/drivers/staging/android/timed_gpio.c |
| 50 | @@ -20,13 +20,12 @@ |
| 51 | #include <linux/err.h> |
| 52 | #include <linux/gpio.h> |
| 53 | |
| 54 | +#include "timed_output.h" |
| 55 | #include "timed_gpio.h" |
| 56 | |
| 57 | |
| 58 | -static struct class *timed_gpio_class; |
| 59 | - |
| 60 | struct timed_gpio_data { |
| 61 | - struct device *dev; |
| 62 | + struct timed_output_dev dev; |
| 63 | struct hrtimer timer; |
| 64 | spinlock_t lock; |
| 65 | unsigned gpio; |
| 66 | @@ -36,70 +35,62 @@ struct timed_gpio_data { |
| 67 | |
| 68 | static enum hrtimer_restart gpio_timer_func(struct hrtimer *timer) |
| 69 | { |
| 70 | - struct timed_gpio_data *gpio_data = container_of(timer, struct timed_gpio_data, timer); |
| 71 | + struct timed_gpio_data *data = |
| 72 | + container_of(timer, struct timed_gpio_data, timer); |
| 73 | |
| 74 | - gpio_direction_output(gpio_data->gpio, gpio_data->active_low ? 1 : 0); |
| 75 | + gpio_direction_output(data->gpio, data->active_low ? 1 : 0); |
| 76 | return HRTIMER_NORESTART; |
| 77 | } |
| 78 | |
| 79 | -static ssize_t gpio_enable_show(struct device *dev, struct device_attribute *attr, char *buf) |
| 80 | +static int gpio_get_time(struct timed_output_dev *dev) |
| 81 | { |
| 82 | - struct timed_gpio_data *gpio_data = dev_get_drvdata(dev); |
| 83 | - int remaining; |
| 84 | + struct timed_gpio_data *data = |
| 85 | + container_of(dev, struct timed_gpio_data, dev); |
| 86 | |
| 87 | - if (hrtimer_active(&gpio_data->timer)) { |
| 88 | - ktime_t r = hrtimer_get_remaining(&gpio_data->timer); |
| 89 | + if (hrtimer_active(&data->timer)) { |
| 90 | + ktime_t r = hrtimer_get_remaining(&data->timer); |
| 91 | struct timeval t = ktime_to_timeval(r); |
| 92 | - remaining = t.tv_sec * 1000 + t.tv_usec / 1000; |
| 93 | + return t.tv_sec * 1000 + t.tv_usec / 1000; |
| 94 | } else |
| 95 | - remaining = 0; |
| 96 | - |
| 97 | - return sprintf(buf, "%d\n", remaining); |
| 98 | + return 0; |
| 99 | } |
| 100 | |
| 101 | -static ssize_t gpio_enable_store( |
| 102 | - struct device *dev, struct device_attribute *attr, |
| 103 | - const char *buf, size_t size) |
| 104 | +static void gpio_enable(struct timed_output_dev *dev, int value) |
| 105 | { |
| 106 | - struct timed_gpio_data *gpio_data = dev_get_drvdata(dev); |
| 107 | - int value; |
| 108 | + struct timed_gpio_data *data = |
| 109 | + container_of(dev, struct timed_gpio_data, dev); |
| 110 | unsigned long flags; |
| 111 | |
| 112 | - sscanf(buf, "%d", &value); |
| 113 | - |
| 114 | - spin_lock_irqsave(&gpio_data->lock, flags); |
| 115 | + spin_lock_irqsave(&data->lock, flags); |
| 116 | |
| 117 | /* cancel previous timer and set GPIO according to value */ |
| 118 | - hrtimer_cancel(&gpio_data->timer); |
| 119 | - gpio_direction_output(gpio_data->gpio, gpio_data->active_low ? !value : !!value); |
| 120 | + hrtimer_cancel(&data->timer); |
| 121 | + gpio_direction_output(data->gpio, data->active_low ? !value : !!value); |
| 122 | |
| 123 | if (value > 0) { |
| 124 | - if (value > gpio_data->max_timeout) |
| 125 | - value = gpio_data->max_timeout; |
| 126 | + if (value > data->max_timeout) |
| 127 | + value = data->max_timeout; |
| 128 | |
| 129 | - hrtimer_start(&gpio_data->timer, |
| 130 | - ktime_set(value / 1000, (value % 1000) * 1000000), |
| 131 | - HRTIMER_MODE_REL); |
| 132 | + hrtimer_start(&data->timer, |
| 133 | + ktime_set(value / 1000, (value % 1000) * 1000000), |
| 134 | + HRTIMER_MODE_REL); |
| 135 | } |
| 136 | |
| 137 | - spin_unlock_irqrestore(&gpio_data->lock, flags); |
| 138 | - |
| 139 | - return size; |
| 140 | + spin_unlock_irqrestore(&data->lock, flags); |
| 141 | } |
| 142 | |
| 143 | -static DEVICE_ATTR(enable, S_IRUGO | S_IWUSR, gpio_enable_show, gpio_enable_store); |
| 144 | - |
| 145 | static int timed_gpio_probe(struct platform_device *pdev) |
| 146 | { |
| 147 | struct timed_gpio_platform_data *pdata = pdev->dev.platform_data; |
| 148 | struct timed_gpio *cur_gpio; |
| 149 | struct timed_gpio_data *gpio_data, *gpio_dat; |
| 150 | - int i, ret = 0; |
| 151 | + int i, j, ret = 0; |
| 152 | |
| 153 | if (!pdata) |
| 154 | return -EBUSY; |
| 155 | |
| 156 | - gpio_data = kzalloc(sizeof(struct timed_gpio_data) * pdata->num_gpios, GFP_KERNEL); |
| 157 | + gpio_data = kzalloc(sizeof(struct timed_gpio_data) * pdata->num_gpios, |
| 158 | + GFP_KERNEL); |
| 159 | if (!gpio_data) |
| 160 | return -ENOMEM; |
| 161 | |
| 162 | @@ -107,23 +98,26 @@ static int timed_gpio_probe(struct platf |
| 163 | cur_gpio = &pdata->gpios[i]; |
| 164 | gpio_dat = &gpio_data[i]; |
| 165 | |
| 166 | - hrtimer_init(&gpio_dat->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); |
| 167 | + hrtimer_init(&gpio_dat->timer, CLOCK_MONOTONIC, |
| 168 | + HRTIMER_MODE_REL); |
| 169 | gpio_dat->timer.function = gpio_timer_func; |
| 170 | spin_lock_init(&gpio_dat->lock); |
| 171 | |
| 172 | + gpio_dat->dev.name = cur_gpio->name; |
| 173 | + gpio_dat->dev.get_time = gpio_get_time; |
| 174 | + gpio_dat->dev.enable = gpio_enable; |
| 175 | + ret = timed_output_dev_register(&gpio_dat->dev); |
| 176 | + if (ret < 0) { |
| 177 | + for (j = 0; j < i; j++) |
| 178 | + timed_output_dev_unregister(&gpio_data[i].dev); |
| 179 | + kfree(gpio_data); |
| 180 | + return ret; |
| 181 | + } |
| 182 | + |
| 183 | gpio_dat->gpio = cur_gpio->gpio; |
| 184 | gpio_dat->max_timeout = cur_gpio->max_timeout; |
| 185 | gpio_dat->active_low = cur_gpio->active_low; |
| 186 | gpio_direction_output(gpio_dat->gpio, gpio_dat->active_low); |
| 187 | - |
| 188 | - gpio_dat->dev = device_create(timed_gpio_class, &pdev->dev, 0, "%s", cur_gpio->name); |
| 189 | - if (unlikely(IS_ERR(gpio_dat->dev))) |
| 190 | - return PTR_ERR(gpio_dat->dev); |
| 191 | - |
| 192 | - dev_set_drvdata(gpio_dat->dev, gpio_dat); |
| 193 | - ret = device_create_file(gpio_dat->dev, &dev_attr_enable); |
| 194 | - if (ret) |
| 195 | - return ret; |
| 196 | } |
| 197 | |
| 198 | platform_set_drvdata(pdev, gpio_data); |
| 199 | @@ -137,10 +131,8 @@ static int timed_gpio_remove(struct plat |
| 200 | struct timed_gpio_data *gpio_data = platform_get_drvdata(pdev); |
| 201 | int i; |
| 202 | |
| 203 | - for (i = 0; i < pdata->num_gpios; i++) { |
| 204 | - device_remove_file(gpio_data[i].dev, &dev_attr_enable); |
| 205 | - device_unregister(gpio_data[i].dev); |
| 206 | - } |
| 207 | + for (i = 0; i < pdata->num_gpios; i++) |
| 208 | + timed_output_dev_unregister(&gpio_data[i].dev); |
| 209 | |
| 210 | kfree(gpio_data); |
| 211 | |
| 212 | @@ -151,22 +143,18 @@ static struct platform_driver timed_gpio |
| 213 | .probe = timed_gpio_probe, |
| 214 | .remove = timed_gpio_remove, |
| 215 | .driver = { |
| 216 | - .name = "timed-gpio", |
| 217 | + .name = TIMED_GPIO_NAME, |
| 218 | .owner = THIS_MODULE, |
| 219 | }, |
| 220 | }; |
| 221 | |
| 222 | static int __init timed_gpio_init(void) |
| 223 | { |
| 224 | - timed_gpio_class = class_create(THIS_MODULE, "timed_output"); |
| 225 | - if (IS_ERR(timed_gpio_class)) |
| 226 | - return PTR_ERR(timed_gpio_class); |
| 227 | return platform_driver_register(&timed_gpio_driver); |
| 228 | } |
| 229 | |
| 230 | static void __exit timed_gpio_exit(void) |
| 231 | { |
| 232 | - class_destroy(timed_gpio_class); |
| 233 | platform_driver_unregister(&timed_gpio_driver); |
| 234 | } |
| 235 | |
| 236 | --- a/drivers/staging/android/timed_gpio.h |
| 237 | +++ b/drivers/staging/android/timed_gpio.h |
| 238 | @@ -16,10 +16,12 @@ |
| 239 | #ifndef _LINUX_TIMED_GPIO_H |
| 240 | #define _LINUX_TIMED_GPIO_H |
| 241 | |
| 242 | +#define TIMED_GPIO_NAME "timed-gpio" |
| 243 | + |
| 244 | struct timed_gpio { |
| 245 | const char *name; |
| 246 | unsigned gpio; |
| 247 | - int max_timeout; |
| 248 | + int max_timeout; |
| 249 | u8 active_low; |
| 250 | }; |
| 251 | |
| 252 | --- /dev/null |
| 253 | +++ b/drivers/staging/android/timed_output.c |
| 254 | @@ -0,0 +1,121 @@ |
| 255 | +/* drivers/misc/timed_output.c |
| 256 | + * |
| 257 | + * Copyright (C) 2009 Google, Inc. |
| 258 | + * Author: Mike Lockwood <lockwood@android.com> |
| 259 | + * |
| 260 | + * This software is licensed under the terms of the GNU General Public |
| 261 | + * License version 2, as published by the Free Software Foundation, and |
| 262 | + * may be copied, distributed, and modified under those terms. |
| 263 | + * |
| 264 | + * This program is distributed in the hope that it will be useful, |
| 265 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 266 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 267 | + * GNU General Public License for more details. |
| 268 | + * |
| 269 | + */ |
| 270 | + |
| 271 | +#include <linux/module.h> |
| 272 | +#include <linux/types.h> |
| 273 | +#include <linux/device.h> |
| 274 | +#include <linux/fs.h> |
| 275 | +#include <linux/err.h> |
| 276 | + |
| 277 | +#include "timed_output.h" |
| 278 | + |
| 279 | +static struct class *timed_output_class; |
| 280 | +static atomic_t device_count; |
| 281 | + |
| 282 | +static ssize_t enable_show(struct device *dev, struct device_attribute *attr, |
| 283 | + char *buf) |
| 284 | +{ |
| 285 | + struct timed_output_dev *tdev = dev_get_drvdata(dev); |
| 286 | + int remaining = tdev->get_time(tdev); |
| 287 | + |
| 288 | + return sprintf(buf, "%d\n", remaining); |
| 289 | +} |
| 290 | + |
| 291 | +static ssize_t enable_store( |
| 292 | + struct device *dev, struct device_attribute *attr, |
| 293 | + const char *buf, size_t size) |
| 294 | +{ |
| 295 | + struct timed_output_dev *tdev = dev_get_drvdata(dev); |
| 296 | + int value; |
| 297 | + |
| 298 | + sscanf(buf, "%d", &value); |
| 299 | + tdev->enable(tdev, value); |
| 300 | + |
| 301 | + return size; |
| 302 | +} |
| 303 | + |
| 304 | +static DEVICE_ATTR(enable, S_IRUGO | S_IWUSR, enable_show, enable_store); |
| 305 | + |
| 306 | +static int create_timed_output_class(void) |
| 307 | +{ |
| 308 | + if (!timed_output_class) { |
| 309 | + timed_output_class = class_create(THIS_MODULE, "timed_output"); |
| 310 | + if (IS_ERR(timed_output_class)) |
| 311 | + return PTR_ERR(timed_output_class); |
| 312 | + atomic_set(&device_count, 0); |
| 313 | + } |
| 314 | + |
| 315 | + return 0; |
| 316 | +} |
| 317 | + |
| 318 | +int timed_output_dev_register(struct timed_output_dev *tdev) |
| 319 | +{ |
| 320 | + int ret; |
| 321 | + |
| 322 | + if (!tdev || !tdev->name || !tdev->enable || !tdev->get_time) |
| 323 | + return -EINVAL; |
| 324 | + |
| 325 | + ret = create_timed_output_class(); |
| 326 | + if (ret < 0) |
| 327 | + return ret; |
| 328 | + |
| 329 | + tdev->index = atomic_inc_return(&device_count); |
| 330 | + tdev->dev = device_create(timed_output_class, NULL, |
| 331 | + MKDEV(0, tdev->index), NULL, tdev->name); |
| 332 | + if (IS_ERR(tdev->dev)) |
| 333 | + return PTR_ERR(tdev->dev); |
| 334 | + |
| 335 | + ret = device_create_file(tdev->dev, &dev_attr_enable); |
| 336 | + if (ret < 0) |
| 337 | + goto err_create_file; |
| 338 | + |
| 339 | + dev_set_drvdata(tdev->dev, tdev); |
| 340 | + tdev->state = 0; |
| 341 | + return 0; |
| 342 | + |
| 343 | +err_create_file: |
| 344 | + device_destroy(timed_output_class, MKDEV(0, tdev->index)); |
| 345 | + printk(KERN_ERR "timed_output: Failed to register driver %s\n", |
| 346 | + tdev->name); |
| 347 | + |
| 348 | + return ret; |
| 349 | +} |
| 350 | +EXPORT_SYMBOL_GPL(timed_output_dev_register); |
| 351 | + |
| 352 | +void timed_output_dev_unregister(struct timed_output_dev *tdev) |
| 353 | +{ |
| 354 | + device_remove_file(tdev->dev, &dev_attr_enable); |
| 355 | + device_destroy(timed_output_class, MKDEV(0, tdev->index)); |
| 356 | + dev_set_drvdata(tdev->dev, NULL); |
| 357 | +} |
| 358 | +EXPORT_SYMBOL_GPL(timed_output_dev_unregister); |
| 359 | + |
| 360 | +static int __init timed_output_init(void) |
| 361 | +{ |
| 362 | + return create_timed_output_class(); |
| 363 | +} |
| 364 | + |
| 365 | +static void __exit timed_output_exit(void) |
| 366 | +{ |
| 367 | + class_destroy(timed_output_class); |
| 368 | +} |
| 369 | + |
| 370 | +module_init(timed_output_init); |
| 371 | +module_exit(timed_output_exit); |
| 372 | + |
| 373 | +MODULE_AUTHOR("Mike Lockwood <lockwood@android.com>"); |
| 374 | +MODULE_DESCRIPTION("timed output class driver"); |
| 375 | +MODULE_LICENSE("GPL"); |
| 376 | --- /dev/null |
| 377 | +++ b/drivers/staging/android/timed_output.h |
| 378 | @@ -0,0 +1,37 @@ |
| 379 | +/* include/linux/timed_output.h |
| 380 | + * |
| 381 | + * Copyright (C) 2008 Google, Inc. |
| 382 | + * |
| 383 | + * This software is licensed under the terms of the GNU General Public |
| 384 | + * License version 2, as published by the Free Software Foundation, and |
| 385 | + * may be copied, distributed, and modified under those terms. |
| 386 | + * |
| 387 | + * This program is distributed in the hope that it will be useful, |
| 388 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 389 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 390 | + * GNU General Public License for more details. |
| 391 | + * |
| 392 | +*/ |
| 393 | + |
| 394 | +#ifndef _LINUX_TIMED_OUTPUT_H |
| 395 | +#define _LINUX_TIMED_OUTPUT_H |
| 396 | + |
| 397 | +struct timed_output_dev { |
| 398 | + const char *name; |
| 399 | + |
| 400 | + /* enable the output and set the timer */ |
| 401 | + void (*enable)(struct timed_output_dev *sdev, int timeout); |
| 402 | + |
| 403 | + /* returns the current number of milliseconds remaining on the timer */ |
| 404 | + int (*get_time)(struct timed_output_dev *sdev); |
| 405 | + |
| 406 | + /* private data */ |
| 407 | + struct device *dev; |
| 408 | + int index; |
| 409 | + int state; |
| 410 | +}; |
| 411 | + |
| 412 | +extern int timed_output_dev_register(struct timed_output_dev *dev); |
| 413 | +extern void timed_output_dev_unregister(struct timed_output_dev *dev); |
| 414 | + |
| 415 | +#endif |
| 416 | |