Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 241e1287 authored by Mike Lockwood's avatar Mike Lockwood Committed by Greg Kroah-Hartman
Browse files

Staging: android: timed_gpio: Separate timed_output class into a separate driver.

parent 5d14a573
Loading
Loading
Loading
Loading
+5 −1
Original line number Diff line number Diff line
@@ -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
+1 −0
Original line number Diff line number Diff line
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
+43 −55
Original line number Diff line number Diff line
@@ -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,
		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 platform_device *pdev)
		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 platform_device *pdev)
	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_driver = {
	.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);
}

+3 −1
Original line number Diff line number Diff line
@@ -16,6 +16,8 @@
#ifndef _LINUX_TIMED_GPIO_H
#define _LINUX_TIMED_GPIO_H

#define TIMED_GPIO_NAME "timed-gpio"

struct timed_gpio {
	const char *name;
	unsigned 	gpio;
+121 −0
Original line number Diff line number Diff line
/* 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");
Loading