timed_output.c revision 02aa2a37636c8fa4fb9322d91be46ff8225b7de0
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#define pr_fmt(fmt) "timed_output: " fmt
18
19#include <linux/module.h>
20#include <linux/types.h>
21#include <linux/device.h>
22#include <linux/fs.h>
23#include <linux/err.h>
24
25#include "timed_output.h"
26
27static struct class *timed_output_class;
28static atomic_t device_count;
29
30static ssize_t enable_show(struct device *dev, struct device_attribute *attr,
31		char *buf)
32{
33	struct timed_output_dev *tdev = dev_get_drvdata(dev);
34	int remaining = tdev->get_time(tdev);
35
36	return sprintf(buf, "%d\n", remaining);
37}
38
39static ssize_t enable_store(
40		struct device *dev, struct device_attribute *attr,
41		const char *buf, size_t size)
42{
43	struct timed_output_dev *tdev = dev_get_drvdata(dev);
44	int value;
45
46	if (sscanf(buf, "%d", &value) != 1)
47		return -EINVAL;
48
49	tdev->enable(tdev, value);
50
51	return size;
52}
53
54static DEVICE_ATTR(enable, S_IRUGO | S_IWUSR, enable_show, enable_store);
55
56static int create_timed_output_class(void)
57{
58	if (!timed_output_class) {
59		timed_output_class = class_create(THIS_MODULE, "timed_output");
60		if (IS_ERR(timed_output_class))
61			return PTR_ERR(timed_output_class);
62		atomic_set(&device_count, 0);
63	}
64
65	return 0;
66}
67
68int timed_output_dev_register(struct timed_output_dev *tdev)
69{
70	int ret;
71
72	if (!tdev || !tdev->name || !tdev->enable || !tdev->get_time)
73		return -EINVAL;
74
75	ret = create_timed_output_class();
76	if (ret < 0)
77		return ret;
78
79	tdev->index = atomic_inc_return(&device_count);
80	tdev->dev = device_create(timed_output_class, NULL,
81		MKDEV(0, tdev->index), NULL, "%s", tdev->name);
82	if (IS_ERR(tdev->dev))
83		return PTR_ERR(tdev->dev);
84
85	ret = device_create_file(tdev->dev, &dev_attr_enable);
86	if (ret < 0)
87		goto err_create_file;
88
89	dev_set_drvdata(tdev->dev, tdev);
90	tdev->state = 0;
91	return 0;
92
93err_create_file:
94	device_destroy(timed_output_class, MKDEV(0, tdev->index));
95	pr_err("failed to register driver %s\n",
96			tdev->name);
97
98	return ret;
99}
100EXPORT_SYMBOL_GPL(timed_output_dev_register);
101
102void timed_output_dev_unregister(struct timed_output_dev *tdev)
103{
104	tdev->enable(tdev, 0);
105	device_remove_file(tdev->dev, &dev_attr_enable);
106	device_destroy(timed_output_class, MKDEV(0, tdev->index));
107	dev_set_drvdata(tdev->dev, NULL);
108}
109EXPORT_SYMBOL_GPL(timed_output_dev_unregister);
110
111static int __init timed_output_init(void)
112{
113	return create_timed_output_class();
114}
115
116static void __exit timed_output_exit(void)
117{
118	class_destroy(timed_output_class);
119}
120
121module_init(timed_output_init);
122module_exit(timed_output_exit);
123
124MODULE_AUTHOR("Mike Lockwood <lockwood@android.com>");
125MODULE_DESCRIPTION("timed output class driver");
126MODULE_LICENSE("GPL");
127