From e4c9c5d7d6d5deb124083678fe5d839d3133f60a Mon Sep 17 00:00:00 2001 From: Mike Lockwood <lockwood@android.com> Date: Mon, 12 Jan 2009 13:25:05 -0500 Subject: [PATCH 054/134] timed_gpio: Separate timed_output class into a separate driver. MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 8bit Signed-off-by: Mike Lockwood <lockwood@android.com> Signed-off-by: Arve Hjønnevåg <arve@android.com> --- drivers/staging/android/Kconfig | 6 ++- drivers/staging/android/Makefile | 1 + drivers/staging/android/timed_gpio.c | 98 +++++++++++-------------- drivers/staging/android/timed_gpio.h | 4 +- drivers/staging/android/timed_output.c | 121 ++++++++++++++++++++++++++++++++ drivers/staging/android/timed_output.h | 37 ++++++++++ 6 files changed, 210 insertions(+), 57 deletions(-) create mode 100644 drivers/staging/android/timed_output.c create mode 100644 drivers/staging/android/timed_output.h --- a/drivers/staging/android/Kconfig +++ b/drivers/staging/android/Kconfig @@ -73,9 +73,13 @@ config ANDROID_RAM_CONSOLE_EARLY_SIZE default 0 depends on ANDROID_RAM_CONSOLE_EARLY_INIT +config ANDROID_TIMED_OUTPUT + bool "Timed output class driver" + default y + config ANDROID_TIMED_GPIO tristate "Android timed gpio driver" - depends on GENERIC_GPIO + depends on GENERIC_GPIO && ANDROID_TIMED_OUTPUT default n config ANDROID_LOW_MEMORY_KILLER --- a/drivers/staging/android/Makefile +++ b/drivers/staging/android/Makefile @@ -1,5 +1,6 @@ obj-$(CONFIG_ANDROID_BINDER_IPC) += binder.o obj-$(CONFIG_ANDROID_LOGGER) += logger.o obj-$(CONFIG_ANDROID_RAM_CONSOLE) += ram_console.o +obj-$(CONFIG_ANDROID_TIMED_OUTPUT) += timed_output.o obj-$(CONFIG_ANDROID_TIMED_GPIO) += timed_gpio.o obj-$(CONFIG_ANDROID_LOW_MEMORY_KILLER) += lowmemorykiller.o --- a/drivers/staging/android/timed_gpio.c +++ b/drivers/staging/android/timed_gpio.c @@ -20,13 +20,12 @@ #include <linux/err.h> #include <linux/gpio.h> +#include "timed_output.h" #include "timed_gpio.h" -static struct class *timed_gpio_class; - struct timed_gpio_data { - struct device *dev; + struct timed_output_dev dev; struct hrtimer timer; spinlock_t lock; unsigned gpio; @@ -36,70 +35,62 @@ struct timed_gpio_data { static enum hrtimer_restart gpio_timer_func(struct hrtimer *timer) { - struct timed_gpio_data *gpio_data = container_of(timer, struct timed_gpio_data, timer); + struct timed_gpio_data *data = + container_of(timer, struct timed_gpio_data, timer); - gpio_direction_output(gpio_data->gpio, gpio_data->active_low ? 1 : 0); + gpio_direction_output(data->gpio, data->active_low ? 1 : 0); return HRTIMER_NORESTART; } -static ssize_t gpio_enable_show(struct device *dev, struct device_attribute *attr, char *buf) +static int gpio_get_time(struct timed_output_dev *dev) { - struct timed_gpio_data *gpio_data = dev_get_drvdata(dev); - int remaining; + struct timed_gpio_data *data = + container_of(dev, struct timed_gpio_data, dev); - if (hrtimer_active(&gpio_data->timer)) { - ktime_t r = hrtimer_get_remaining(&gpio_data->timer); + if (hrtimer_active(&data->timer)) { + ktime_t r = hrtimer_get_remaining(&data->timer); struct timeval t = ktime_to_timeval(r); - remaining = t.tv_sec * 1000 + t.tv_usec / 1000; + return t.tv_sec * 1000 + t.tv_usec / 1000; } else - remaining = 0; - - return sprintf(buf, "%d\n", remaining); + return 0; } -static ssize_t gpio_enable_store( - struct device *dev, struct device_attribute *attr, - const char *buf, size_t size) +static void gpio_enable(struct timed_output_dev *dev, int value) { - struct timed_gpio_data *gpio_data = dev_get_drvdata(dev); - int value; + struct timed_gpio_data *data = + container_of(dev, struct timed_gpio_data, dev); unsigned long flags; - sscanf(buf, "%d", &value); - - spin_lock_irqsave(&gpio_data->lock, flags); + spin_lock_irqsave(&data->lock, flags); /* cancel previous timer and set GPIO according to value */ - hrtimer_cancel(&gpio_data->timer); - gpio_direction_output(gpio_data->gpio, gpio_data->active_low ? !value : !!value); + hrtimer_cancel(&data->timer); + gpio_direction_output(data->gpio, data->active_low ? !value : !!value); if (value > 0) { - if (value > gpio_data->max_timeout) - value = gpio_data->max_timeout; + if (value > data->max_timeout) + value = data->max_timeout; - hrtimer_start(&gpio_data->timer, - ktime_set(value / 1000, (value % 1000) * 1000000), - HRTIMER_MODE_REL); + hrtimer_start(&data->timer, + ktime_set(value / 1000, (value % 1000) * 1000000), + HRTIMER_MODE_REL); } - spin_unlock_irqrestore(&gpio_data->lock, flags); - - return size; + spin_unlock_irqrestore(&data->lock, flags); } -static DEVICE_ATTR(enable, S_IRUGO | S_IWUSR, gpio_enable_show, gpio_enable_store); - static int timed_gpio_probe(struct platform_device *pdev) { struct timed_gpio_platform_data *pdata = pdev->dev.platform_data; struct timed_gpio *cur_gpio; struct timed_gpio_data *gpio_data, *gpio_dat; - int i, ret = 0; + int i, j, ret = 0; if (!pdata) return -EBUSY; - gpio_data = kzalloc(sizeof(struct timed_gpio_data) * pdata->num_gpios, GFP_KERNEL); + gpio_data = kzalloc(sizeof(struct timed_gpio_data) * pdata->num_gpios, + GFP_KERNEL); if (!gpio_data) return -ENOMEM; @@ -107,23 +98,26 @@ static int timed_gpio_probe(struct platf cur_gpio = &pdata->gpios[i]; gpio_dat = &gpio_data[i]; - hrtimer_init(&gpio_dat->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); + hrtimer_init(&gpio_dat->timer, CLOCK_MONOTONIC, + HRTIMER_MODE_REL); gpio_dat->timer.function = gpio_timer_func; spin_lock_init(&gpio_dat->lock); + gpio_dat->dev.name = cur_gpio->name; + gpio_dat->dev.get_time = gpio_get_time; + gpio_dat->dev.enable = gpio_enable; + ret = timed_output_dev_register(&gpio_dat->dev); + if (ret < 0) { + for (j = 0; j < i; j++) + timed_output_dev_unregister(&gpio_data[i].dev); + kfree(gpio_data); + return ret; + } + gpio_dat->gpio = cur_gpio->gpio; gpio_dat->max_timeout = cur_gpio->max_timeout; gpio_dat->active_low = cur_gpio->active_low; gpio_direction_output(gpio_dat->gpio, gpio_dat->active_low); - - gpio_dat->dev = device_create(timed_gpio_class, &pdev->dev, 0, "%s", cur_gpio->name); - if (unlikely(IS_ERR(gpio_dat->dev))) - return PTR_ERR(gpio_dat->dev); - - dev_set_drvdata(gpio_dat->dev, gpio_dat); - ret = device_create_file(gpio_dat->dev, &dev_attr_enable); - if (ret) - return ret; } platform_set_drvdata(pdev, gpio_data); @@ -137,10 +131,8 @@ static int timed_gpio_remove(struct plat struct timed_gpio_data *gpio_data = platform_get_drvdata(pdev); int i; - for (i = 0; i < pdata->num_gpios; i++) { - device_remove_file(gpio_data[i].dev, &dev_attr_enable); - device_unregister(gpio_data[i].dev); - } + for (i = 0; i < pdata->num_gpios; i++) + timed_output_dev_unregister(&gpio_data[i].dev); kfree(gpio_data); @@ -151,22 +143,18 @@ static struct platform_driver timed_gpio .probe = timed_gpio_probe, .remove = timed_gpio_remove, .driver = { - .name = "timed-gpio", + .name = TIMED_GPIO_NAME, .owner = THIS_MODULE, }, }; static int __init timed_gpio_init(void) { - timed_gpio_class = class_create(THIS_MODULE, "timed_output"); - if (IS_ERR(timed_gpio_class)) - return PTR_ERR(timed_gpio_class); return platform_driver_register(&timed_gpio_driver); } static void __exit timed_gpio_exit(void) { - class_destroy(timed_gpio_class); platform_driver_unregister(&timed_gpio_driver); } --- a/drivers/staging/android/timed_gpio.h +++ b/drivers/staging/android/timed_gpio.h @@ -16,10 +16,12 @@ #ifndef _LINUX_TIMED_GPIO_H #define _LINUX_TIMED_GPIO_H +#define TIMED_GPIO_NAME "timed-gpio" + struct timed_gpio { const char *name; unsigned gpio; - int max_timeout; + int max_timeout; u8 active_low; }; --- /dev/null +++ b/drivers/staging/android/timed_output.c @@ -0,0 +1,121 @@ +/* drivers/misc/timed_output.c + * + * Copyright (C) 2009 Google, Inc. + * Author: Mike Lockwood <lockwood@android.com> + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include <linux/module.h> +#include <linux/types.h> +#include <linux/device.h> +#include <linux/fs.h> +#include <linux/err.h> + +#include "timed_output.h" + +static struct class *timed_output_class; +static atomic_t device_count; + +static ssize_t enable_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct timed_output_dev *tdev = dev_get_drvdata(dev); + int remaining = tdev->get_time(tdev); + + return sprintf(buf, "%d\n", remaining); +} + +static ssize_t enable_store( + struct device *dev, struct device_attribute *attr, + const char *buf, size_t size) +{ + struct timed_output_dev *tdev = dev_get_drvdata(dev); + int value; + + sscanf(buf, "%d", &value); + tdev->enable(tdev, value); + + return size; +} + +static DEVICE_ATTR(enable, S_IRUGO | S_IWUSR, enable_show, enable_store); + +static int create_timed_output_class(void) +{ + if (!timed_output_class) { + timed_output_class = class_create(THIS_MODULE, "timed_output"); + if (IS_ERR(timed_output_class)) + return PTR_ERR(timed_output_class); + atomic_set(&device_count, 0); + } + + return 0; +} + +int timed_output_dev_register(struct timed_output_dev *tdev) +{ + int ret; + + if (!tdev || !tdev->name || !tdev->enable || !tdev->get_time) + return -EINVAL; + + ret = create_timed_output_class(); + if (ret < 0) + return ret; + + tdev->index = atomic_inc_return(&device_count); + tdev->dev = device_create(timed_output_class, NULL, + MKDEV(0, tdev->index), NULL, tdev->name); + if (IS_ERR(tdev->dev)) + return PTR_ERR(tdev->dev); + + ret = device_create_file(tdev->dev, &dev_attr_enable); + if (ret < 0) + goto err_create_file; + + dev_set_drvdata(tdev->dev, tdev); + tdev->state = 0; + return 0; + +err_create_file: + device_destroy(timed_output_class, MKDEV(0, tdev->index)); + printk(KERN_ERR "timed_output: Failed to register driver %s\n", + tdev->name); + + return ret; +} +EXPORT_SYMBOL_GPL(timed_output_dev_register); + +void timed_output_dev_unregister(struct timed_output_dev *tdev) +{ + device_remove_file(tdev->dev, &dev_attr_enable); + device_destroy(timed_output_class, MKDEV(0, tdev->index)); + dev_set_drvdata(tdev->dev, NULL); +} +EXPORT_SYMBOL_GPL(timed_output_dev_unregister); + +static int __init timed_output_init(void) +{ + return create_timed_output_class(); +} + +static void __exit timed_output_exit(void) +{ + class_destroy(timed_output_class); +} + +module_init(timed_output_init); +module_exit(timed_output_exit); + +MODULE_AUTHOR("Mike Lockwood <lockwood@android.com>"); +MODULE_DESCRIPTION("timed output class driver"); +MODULE_LICENSE("GPL"); --- /dev/null +++ b/drivers/staging/android/timed_output.h @@ -0,0 +1,37 @@ +/* include/linux/timed_output.h + * + * Copyright (C) 2008 Google, Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * +*/ + +#ifndef _LINUX_TIMED_OUTPUT_H +#define _LINUX_TIMED_OUTPUT_H + +struct timed_output_dev { + const char *name; + + /* enable the output and set the timer */ + void (*enable)(struct timed_output_dev *sdev, int timeout); + + /* returns the current number of milliseconds remaining on the timer */ + int (*get_time)(struct timed_output_dev *sdev); + + /* private data */ + struct device *dev; + int index; + int state; +}; + +extern int timed_output_dev_register(struct timed_output_dev *dev); +extern void timed_output_dev_unregister(struct timed_output_dev *dev); + +#endif