upload tizen1.0 source
[kernel/linux-2.6.36.git] / drivers / switch / switch_class.c
1 /*
2  *  drivers/switch/switch_class.c
3  *
4  * Copyright (C) 2008 Google, Inc.
5  * Author: Mike Lockwood <lockwood@android.com>
6  *
7  * This software is licensed under the terms of the GNU General Public
8  * License version 2, as published by the Free Software Foundation, and
9  * may be copied, distributed, and modified under those terms.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16 */
17
18 #include <linux/module.h>
19 #include <linux/types.h>
20 #include <linux/init.h>
21 #include <linux/device.h>
22 #include <linux/fs.h>
23 #include <linux/err.h>
24 #include <linux/switch.h>
25
26 struct class *switch_class;
27 static atomic_t device_count;
28
29 static LIST_HEAD(switch_dev_list);
30 static DEFINE_MUTEX(switch_dev_list_lock);
31
32 static ssize_t state_show(struct device *dev, struct device_attribute *attr,
33                 char *buf)
34 {
35         struct switch_dev *sdev = (struct switch_dev *)
36                 dev_get_drvdata(dev);
37
38         if (sdev->print_state) {
39                 int ret = sdev->print_state(sdev, buf);
40                 if (ret >= 0)
41                         return ret;
42         }
43         return sprintf(buf, "%d\n", sdev->state);
44 }
45
46 static ssize_t name_show(struct device *dev, struct device_attribute *attr,
47                 char *buf)
48 {
49         struct switch_dev *sdev = (struct switch_dev *)
50                 dev_get_drvdata(dev);
51
52         if (sdev->print_name) {
53                 int ret = sdev->print_name(sdev, buf);
54                 if (ret >= 0)
55                         return ret;
56         }
57         return sprintf(buf, "%s\n", sdev->name);
58 }
59
60 static DEVICE_ATTR(state, S_IRUGO | S_IWUSR, state_show, NULL);
61 static DEVICE_ATTR(name, S_IRUGO | S_IWUSR, name_show, NULL);
62
63 void switch_set_state(struct switch_dev *sdev, int state)
64 {
65         char name_buf[120];
66         char state_buf[120];
67         char *prop_buf;
68         char *envp[3];
69         int env_offset = 0;
70         int length;
71
72         if (sdev->state != state) {
73                 sdev->state = state;
74
75                 raw_notifier_call_chain(&sdev->nh, sdev->state, NULL);
76
77                 prop_buf = (char *)get_zeroed_page(GFP_KERNEL);
78                 if (prop_buf) {
79                         length = name_show(sdev->dev, NULL, prop_buf);
80                         if (length > 0) {
81                                 if (prop_buf[length - 1] == '\n')
82                                         prop_buf[length - 1] = 0;
83                                 snprintf(name_buf, sizeof(name_buf),
84                                         "SWITCH_NAME=%s", prop_buf);
85                                 envp[env_offset++] = name_buf;
86                         }
87                         length = state_show(sdev->dev, NULL, prop_buf);
88                         if (length > 0) {
89                                 if (prop_buf[length - 1] == '\n')
90                                         prop_buf[length - 1] = 0;
91                                 snprintf(state_buf, sizeof(state_buf),
92                                         "SWITCH_STATE=%s", prop_buf);
93                                 envp[env_offset++] = state_buf;
94                         }
95                         envp[env_offset] = NULL;
96                         kobject_uevent_env(&sdev->dev->kobj, KOBJ_CHANGE, envp);
97                         free_page((unsigned long)prop_buf);
98
99                 } else {
100                         printk(KERN_ERR "out of memory in switch_set_state\n");
101                         kobject_uevent(&sdev->dev->kobj, KOBJ_CHANGE);
102                 }
103         }
104 }
105 EXPORT_SYMBOL_GPL(switch_set_state);
106
107 static int switch_find_cable_index(struct switch_dev *sdev, const char *cable_name)
108 {
109         int i;
110
111         if (sdev->supported_cable) {
112                 for (i = 0; sdev->supported_cable[i]; i++) {
113                         if (!strncmp(sdev->supported_cable[i],
114                                 cable_name, CABLE_NAME_MAX))
115                                 return i;
116                 }
117         }
118
119         return -1;
120 }
121
122 int switch_set_cable_state(struct switch_dev *sdev,
123                         const char *cable_name, int cable_state)
124 {
125         int ret = -EINVAL, state;
126         int index = switch_find_cable_index(sdev, cable_name);
127
128         if (index >= 0) {
129                 if (cable_state)
130                         state = sdev->state | (1 << index);
131                 else
132                         state = ~(~sdev->state | (1 << index));
133                 switch_set_state(sdev, state);
134                 ret = 0;
135         }
136
137         return ret;
138 }
139 EXPORT_SYMBOL_GPL(switch_set_cable_state);
140
141 int switch_get_cable_state(struct switch_dev *sdev, const char *cable_name)
142 {
143         int ret = -EINVAL, cable_state;
144         int index = switch_find_cable_index(sdev, cable_name);
145
146         if (index >= 0) {
147                 cable_state = sdev->state & (1 << index);
148                 ret = cable_state ? 1 : 0;
149         }
150
151         return ret;
152 }
153 EXPORT_SYMBOL_GPL(switch_get_cable_state);
154
155 int switch_get_event_code(struct switch_dev *sdev, const char *cable_name)
156 {
157         int ret = -EINVAL;
158         int index = switch_find_cable_index(sdev, cable_name);
159
160         if (index >= 0)
161                 ret = 1 << index;
162
163         return ret;
164 }
165 EXPORT_SYMBOL_GPL(switch_get_event_code);
166
167 struct switch_dev *switch_get_switch_dev(const char *switch_name)
168 {
169         struct switch_dev *sd;
170
171         mutex_lock(&switch_dev_list_lock);
172         list_for_each_entry(sd, &switch_dev_list, entry) {
173                 if (!strcmp(sd->name, switch_name))
174                         goto out;
175         }
176         sd = NULL;
177 out:
178         mutex_unlock(&switch_dev_list_lock);
179         return sd;
180 }
181 EXPORT_SYMBOL_GPL(switch_get_switch_dev);
182
183 int switch_register_notifier(struct switch_dev *sdev,
184                         struct notifier_block *bh)
185 {
186         return raw_notifier_chain_register(&sdev->nh, bh);
187 }
188 EXPORT_SYMBOL_GPL(switch_register_notifier);
189
190 int switch_unregister_notifier(struct switch_dev *sdev,
191                         struct notifier_block *bh)
192 {
193         return raw_notifier_chain_register(&sdev->nh, bh);
194 }
195 EXPORT_SYMBOL_GPL(switch_unregister_notifier);
196
197 static int create_switch_class(void)
198 {
199         if (!switch_class) {
200                 switch_class = class_create(THIS_MODULE, "switch");
201                 if (IS_ERR(switch_class))
202                         return PTR_ERR(switch_class);
203                 atomic_set(&device_count, 0);
204         }
205
206         return 0;
207 }
208
209 int switch_dev_register(struct switch_dev *sdev)
210 {
211         int ret, index = 0;
212
213         if (!switch_class) {
214                 ret = create_switch_class();
215                 if (ret < 0)
216                         return ret;
217         }
218
219         if (sdev->supported_cable) {
220                 /* Get size of array */
221                 for (index = 0; sdev->supported_cable[index]; index++)
222
223                 if (index > SUPPORTED_CABLE_MAX) {
224                         printk(KERN_ERR
225                         "switch: Exceed maximum number of supported cable\n");
226                         return -EINVAL;
227                 }
228         }
229
230         if (index > SUPPORTED_CABLE_MAX) {
231                 printk(KERN_ERR
232                         "switch: Exceed maximum number of supported cable\n");
233                 return -EINVAL;
234         }
235
236         sdev->index = atomic_inc_return(&device_count);
237         sdev->dev = device_create(switch_class, NULL,
238                 MKDEV(0, sdev->index), NULL, sdev->name);
239         if (IS_ERR(sdev->dev))
240                 return PTR_ERR(sdev->dev);
241
242         ret = device_create_file(sdev->dev, &dev_attr_state);
243         if (ret < 0)
244                 goto err_create_file_1;
245         ret = device_create_file(sdev->dev, &dev_attr_name);
246         if (ret < 0)
247                 goto err_create_file_2;
248
249         RAW_INIT_NOTIFIER_HEAD(&sdev->nh);
250
251         dev_set_drvdata(sdev->dev, sdev);
252         sdev->state = 0;
253
254         mutex_lock(&switch_dev_list_lock);
255         list_add(&sdev->entry, &switch_dev_list);
256         mutex_unlock(&switch_dev_list_lock);
257
258         return 0;
259
260 err_create_file_2:
261         device_remove_file(sdev->dev, &dev_attr_state);
262 err_create_file_1:
263         device_destroy(switch_class, MKDEV(0, sdev->index));
264         printk(KERN_ERR "switch: Failed to register driver %s\n", sdev->name);
265
266         return ret;
267 }
268 EXPORT_SYMBOL_GPL(switch_dev_register);
269
270 void switch_dev_unregister(struct switch_dev *sdev)
271 {
272         device_remove_file(sdev->dev, &dev_attr_name);
273         device_remove_file(sdev->dev, &dev_attr_state);
274         device_destroy(switch_class, MKDEV(0, sdev->index));
275
276         mutex_lock(&switch_dev_list_lock);
277         list_del(&sdev->entry);
278         mutex_unlock(&switch_dev_list_lock);
279
280         dev_set_drvdata(sdev->dev, NULL);
281 }
282 EXPORT_SYMBOL_GPL(switch_dev_unregister);
283
284 static int __init switch_class_init(void)
285 {
286         return create_switch_class();
287 }
288
289 static void __exit switch_class_exit(void)
290 {
291         class_destroy(switch_class);
292 }
293
294 module_init(switch_class_init);
295 module_exit(switch_class_exit);
296
297 MODULE_AUTHOR("Mike Lockwood <lockwood@android.com>");
298 MODULE_DESCRIPTION("Switch class driver");
299 MODULE_LICENSE("GPL");