blob: f7db54c1b39308e74f6aa1b9e1e3c564a6513df0 [file] [log] [blame]
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001/* Copyright (c) 2011, Code Aurora Forum. All rights reserved.
2 *
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License version 2 and
5 * only version 2 as published by the Free Software Foundation.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 *
12 *
13 * Subsystem Notifier -- Provides notifications
14 * of subsys events.
15 *
16 * Use subsys_notif_register_notifier to register for notifications
17 * and subsys_notif_queue_notification to send notifications.
18 *
19 */
20
21#include <linux/notifier.h>
22#include <linux/init.h>
23#include <linux/debugfs.h>
24#include <linux/module.h>
25#include <linux/workqueue.h>
26#include <linux/stringify.h>
27#include <linux/delay.h>
28#include <linux/slab.h>
29
30#include <mach/subsystem_notif.h>
31
32struct subsys_notif_info {
33 char name[50];
34 struct srcu_notifier_head subsys_notif_rcvr_list;
35 struct list_head list;
36};
37
38static LIST_HEAD(subsystem_list);
39static DEFINE_MUTEX(notif_lock);
40static DEFINE_MUTEX(notif_add_lock);
41
42#if defined(SUBSYS_RESTART_DEBUG)
43static void subsys_notif_reg_test_notifier(const char *);
44#endif
45
46static struct subsys_notif_info *_notif_find_subsys(const char *subsys_name)
47{
48 struct subsys_notif_info *subsys;
49
50 mutex_lock(&notif_lock);
51 list_for_each_entry(subsys, &subsystem_list, list)
52 if (!strncmp(subsys->name, subsys_name,
53 ARRAY_SIZE(subsys->name))) {
54 mutex_unlock(&notif_lock);
55 return subsys;
56 }
57 mutex_unlock(&notif_lock);
58
59 return NULL;
60}
61
62void *subsys_notif_register_notifier(
63 const char *subsys_name, struct notifier_block *nb)
64{
65 int ret;
66 struct subsys_notif_info *subsys = _notif_find_subsys(subsys_name);
67
68 if (!subsys) {
69
70 /* Possible first time reference to this subsystem. Add it. */
71 subsys = (struct subsys_notif_info *)
72 subsys_notif_add_subsys(subsys_name);
73
74 if (!subsys)
75 return ERR_PTR(-EINVAL);
76 }
77
78 ret = srcu_notifier_chain_register(
79 &subsys->subsys_notif_rcvr_list, nb);
80
81 if (ret < 0)
82 return ERR_PTR(ret);
83
84 return subsys;
85}
86EXPORT_SYMBOL(subsys_notif_register_notifier);
87
88int subsys_notif_unregister_notifier(void *subsys_handle,
89 struct notifier_block *nb)
90{
91 int ret;
92 struct subsys_notif_info *subsys =
93 (struct subsys_notif_info *)subsys_handle;
94
95 if (!subsys)
96 return -EINVAL;
97
98 ret = srcu_notifier_chain_unregister(
99 &subsys->subsys_notif_rcvr_list, nb);
100
101 return ret;
102}
103EXPORT_SYMBOL(subsys_notif_unregister_notifier);
104
105void *subsys_notif_add_subsys(const char *subsys_name)
106{
107 struct subsys_notif_info *subsys = NULL;
108
109 if (!subsys_name)
110 goto done;
111
112 mutex_lock(&notif_add_lock);
113
114 subsys = _notif_find_subsys(subsys_name);
115
116 if (subsys) {
117 mutex_unlock(&notif_add_lock);
118 goto done;
119 }
120
121 subsys = kmalloc(sizeof(struct subsys_notif_info), GFP_KERNEL);
122
123 if (!subsys) {
124 mutex_unlock(&notif_add_lock);
125 return ERR_PTR(-EINVAL);
126 }
127
128 strlcpy(subsys->name, subsys_name, ARRAY_SIZE(subsys->name));
129
130 srcu_init_notifier_head(&subsys->subsys_notif_rcvr_list);
131
132 INIT_LIST_HEAD(&subsys->list);
133
134 mutex_lock(&notif_lock);
135 list_add_tail(&subsys->list, &subsystem_list);
136 mutex_unlock(&notif_lock);
137
138 #if defined(SUBSYS_RESTART_DEBUG)
139 subsys_notif_reg_test_notifier(subsys->name);
140 #endif
141
142 mutex_unlock(&notif_add_lock);
143
144done:
145 return subsys;
146}
147EXPORT_SYMBOL(subsys_notif_add_subsys);
148
149int subsys_notif_queue_notification(void *subsys_handle,
150 enum subsys_notif_type notif_type)
151{
152 int ret = 0;
153 struct subsys_notif_info *subsys =
154 (struct subsys_notif_info *) subsys_handle;
155
156 if (!subsys)
157 return -EINVAL;
158
159 if (notif_type < 0 || notif_type >= SUBSYS_NOTIF_TYPE_COUNT)
160 return -EINVAL;
161
162 ret = srcu_notifier_call_chain(
163 &subsys->subsys_notif_rcvr_list, notif_type,
164 (void *)subsys);
165
166 return ret;
167}
168EXPORT_SYMBOL(subsys_notif_queue_notification);
169
170#if defined(SUBSYS_RESTART_DEBUG)
171static const char *notif_to_string(enum subsys_notif_type notif_type)
172{
173 switch (notif_type) {
174
175 case SUBSYS_BEFORE_SHUTDOWN:
176 return __stringify(SUBSYS_BEFORE_SHUTDOWN);
177
178 case SUBSYS_AFTER_SHUTDOWN:
179 return __stringify(SUBSYS_AFTER_SHUTDOWN);
180
181 case SUBSYS_BEFORE_POWERUP:
182 return __stringify(SUBSYS_BEFORE_POWERUP);
183
184 case SUBSYS_AFTER_POWERUP:
185 return __stringify(SUBSYS_AFTER_POWERUP);
186
187 default:
188 return "unknown";
189 }
190}
191
192static int subsys_notifier_test_call(struct notifier_block *this,
193 unsigned long code,
194 void *data)
195{
196 switch (code) {
197
198 default:
199 printk(KERN_WARNING "%s: Notification %s from subsystem %p\n",
200 __func__, notif_to_string(code), data);
201 break;
202
203 }
204
205 return NOTIFY_DONE;
206}
207
208static struct notifier_block nb = {
209 .notifier_call = subsys_notifier_test_call,
210};
211
212static void subsys_notif_reg_test_notifier(const char *subsys_name)
213{
214 void *handle = subsys_notif_register_notifier(subsys_name, &nb);
215 printk(KERN_WARNING "%s: Registered test notifier, handle=%p",
216 __func__, handle);
217}
218#endif
219
220MODULE_DESCRIPTION("Subsystem Restart Notifier");
221MODULE_VERSION("1.0");
222MODULE_LICENSE("GPL v2");