timed_output.c revision 8bfe15f3de0af92fd822b7475878bddc93fa490d
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
25static struct class *timed_output_class;
26static atomic_t device_count;
27
28static 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
37static 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	if (sscanf(buf, "%d", &value) != 1)
45		return -EINVAL;
46
47	tdev->enable(tdev, value);
48
49	return size;
50}
51
52static DEVICE_ATTR(enable, S_IRUGO | S_IWUSR, enable_show, enable_store);
53
54static int create_timed_output_class(void)
55{
56	if (!timed_output_class) {
57		timed_output_class = class_create(THIS_MODULE, "timed_output");
58		if (IS_ERR(timed_output_class))
59			return PTR_ERR(timed_output_class);
60		atomic_set(&device_count, 0);
61	}
62
63	return 0;
64}
65
66int timed_output_dev_register(struct timed_output_dev *tdev)
67{
68	int ret;
69
70	if (!tdev || !tdev->name || !tdev->enable || !tdev->get_time)
71		return -EINVAL;
72
73	ret = create_timed_output_class();
74	if (ret < 0)
75		return ret;
76
77	tdev->index = atomic_inc_return(&device_count);
78	tdev->dev = device_create(timed_output_class, NULL,
79		MKDEV(0, tdev->index), NULL, tdev->name);
80	if (IS_ERR(tdev->dev))
81		return PTR_ERR(tdev->dev);
82
83	ret = device_create_file(tdev->dev, &dev_attr_enable);
84	if (ret < 0)
85		goto err_create_file;
86
87	dev_set_drvdata(tdev->dev, tdev);
88	tdev->state = 0;
89	return 0;
90
91err_create_file:
92	device_destroy(timed_output_class, MKDEV(0, tdev->index));
93	printk(KERN_ERR "timed_output: Failed to register driver %s\n",
94			tdev->name);
95
96	return ret;
97}
98EXPORT_SYMBOL_GPL(timed_output_dev_register);
99
100void timed_output_dev_unregister(struct timed_output_dev *tdev)
101{
102	device_remove_file(tdev->dev, &dev_attr_enable);
103	device_destroy(timed_output_class, MKDEV(0, tdev->index));
104	dev_set_drvdata(tdev->dev, NULL);
105}
106EXPORT_SYMBOL_GPL(timed_output_dev_unregister);
107
108static int __init timed_output_init(void)
109{
110	return create_timed_output_class();
111}
112
113static void __exit timed_output_exit(void)
114{
115	class_destroy(timed_output_class);
116}
117
118module_init(timed_output_init);
119module_exit(timed_output_exit);
120
121MODULE_AUTHOR("Mike Lockwood <lockwood@android.com>");
122MODULE_DESCRIPTION("timed output class driver");
123MODULE_LICENSE("GPL");
124