099f4d1c3cc460dd345f940bafa07ad41eb1aad0
[platform/hal/backend/tw3/device-tw3.git] / hw / udev.c
1 /*
2  * device-manager
3  *
4  * Copyright (c) 2015 Samsung Electronics Co., Ltd.
5  *
6  * Licensed under the Apache License, Version 2.0 (the License);
7  * you may not use this file except in compliance with the License.
8  * You may obtain a copy of the License at
9  *
10  *     http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and
16  * limitations under the License.
17  */
18
19
20 #include <stdio.h>
21 #include <stdbool.h>
22 #include <errno.h>
23 #include <libudev.h>
24 #include <glib.h>
25 #include <string.h>
26
27 #include "udev.h"
28
29 #include </hal/include/device/hal-backend-common.h>
30
31 #define EVENT_KERNEL       "kernel"
32 #define EVENT_UDEV         "udev"
33
34 #define UDEV_MONITOR_SIZE   (128*1024)
35
36 struct uevent_info {
37         struct udev_monitor *mon;
38         GIOChannel *ch;
39         guint eventid;
40         GList *event_list;
41 };
42
43
44 /* Uevent */
45 static struct udev *udev;
46 static struct uevent_info kevent; /* kernel */
47 static struct uevent_info uevent; /* udev */
48
49 static gboolean uevent_control_cb(GIOChannel *channel,
50                 GIOCondition cond, void *data)
51 {
52         struct uevent_info *info = data;
53         struct udev_device *dev;
54         struct uevent_handler *l;
55         GList *elem;
56         const char *subsystem;
57         int len;
58
59         if (!info) {
60                 _E("data is invalid");
61                 return TRUE;
62         }
63
64         dev = udev_monitor_receive_device(info->mon);
65         if (!dev)
66                 return TRUE;
67
68         subsystem = udev_device_get_subsystem(dev);
69         if (!subsystem)
70                 goto out;
71
72         len = strlen(subsystem);
73
74         for (elem = info->event_list ; elem ; elem = g_list_next(elem)) {
75                 l = elem->data;
76                 if (!l)
77                         continue;
78                 if (!strncmp(l->subsystem, subsystem, len) &&
79                     l->uevent_func)
80                         l->uevent_func(dev);
81         }
82
83 out:
84         udev_device_unref(dev);
85         return TRUE;
86 }
87
88 static int uevent_control_stop(struct uevent_info *info)
89 {
90         struct udev_device *dev;
91
92         if (!info)
93                 return -EINVAL;
94
95         if (info->eventid) {
96                 g_source_remove(info->eventid);
97                 info->eventid = 0;
98         }
99         if (info->ch) {
100                 g_io_channel_unref(info->ch);
101                 info->ch = NULL;
102         }
103         if (info->mon) {
104                 dev = udev_monitor_receive_device(info->mon);
105                 if (dev)
106                         udev_device_unref(dev);
107                 udev_monitor_unref(info->mon);
108                 info->mon = NULL;
109         }
110         if (udev)
111                 udev = udev_unref(udev);
112         return 0;
113 }
114
115 static int uevent_control_start(const char *type,
116                 struct uevent_info *info)
117 {
118         struct uevent_handler *l;
119         GList *elem;
120         int fd;
121         int ret;
122
123         if (!info)
124                 return -EINVAL;
125
126         if (info->mon) {
127                 _E("%s uevent control routine is alreay started", type);
128                 return -EINVAL;
129         }
130
131         if (!udev) {
132                 udev = udev_new();
133                 if (!udev) {
134                         _E("error create udev");
135                         return -EINVAL;
136                 }
137         } else
138                 udev = udev_ref(udev);
139
140         info->mon = udev_monitor_new_from_netlink(udev, type);
141         if (info->mon == NULL) {
142                 _E("error udev_monitor create");
143                 goto stop;
144         }
145
146         _I("Set udev monitor buffer size %d", UDEV_MONITOR_SIZE);
147         ret = udev_monitor_set_receive_buffer_size(info->mon,
148                         UDEV_MONITOR_SIZE);
149         if (ret != 0) {
150                 _E("fail to set receive buffer size");
151                 goto stop;
152         }
153
154         for (elem = info->event_list ; elem ; elem = g_list_next(elem)) {
155                 l = elem->data;
156                 ret = udev_monitor_filter_add_match_subsystem_devtype(
157                                 info->mon,
158                                 l->subsystem, NULL);
159                 if (ret < 0) {
160                         _E("error apply subsystem filter");
161                         goto stop;
162                 }
163         }
164
165         ret = udev_monitor_filter_update(info->mon);
166         if (ret < 0)
167                 _E("error udev_monitor_filter_update");
168
169         fd = udev_monitor_get_fd(info->mon);
170         if (fd == -1) {
171                 _E("error udev_monitor_get_fd");
172                 goto stop;
173         }
174
175         info->ch = g_io_channel_unix_new(fd);
176         info->eventid = g_io_add_watch(info->ch,
177                         G_IO_IN, uevent_control_cb, info);
178         if (info->eventid == 0) {
179                 _E("Failed to add channel watch");
180                 goto stop;
181         }
182
183         if (udev_monitor_enable_receiving(info->mon) < 0) {
184                 _E("error unable to subscribe to udev events");
185                 goto stop;
186         }
187
188         return 0;
189 stop:
190         uevent_control_stop(info);
191         return -EINVAL;
192 }
193
194 int uevent_control_kernel_start(void)
195 {
196         return uevent_control_start(EVENT_KERNEL, &kevent);
197 }
198
199 void uevent_control_kernel_stop(void)
200 {
201         uevent_control_stop(&kevent);
202 }
203
204 int uevent_control_udev_start(void)
205 {
206         return uevent_control_start(EVENT_UDEV, &uevent);
207 }
208
209 void uevent_control_udev_stop(void)
210 {
211         uevent_control_stop(&uevent);
212 }
213
214 static int register_uevent_control(struct uevent_info *info,
215                 struct uevent_handler *uh)
216 {
217         struct uevent_handler *l;
218         GList *elem;
219         int r;
220         bool matched = false;
221         int len;
222
223         if (!info || !uh || !uh->subsystem)
224                 return -EINVAL;
225
226         /* if udev is not initialized, it just will be added list */
227         if (!udev || !info->mon)
228                 goto add_list;
229
230         len = strlen(uh->subsystem);
231         /* check if the same subsystem is already added */
232         for (elem = info->event_list; elem ; elem = g_list_next(elem)) {
233                 l = elem->data;
234                 if (!strncmp(l->subsystem, uh->subsystem, len)) {
235                         matched = true;
236                         break;
237                 }
238         }
239
240         /* the first request to add subsystem */
241         if (!matched) {
242                 r = udev_monitor_filter_add_match_subsystem_devtype(info->mon,
243                                 uh->subsystem, NULL);
244                 if (r < 0) {
245                         _E("fail to add %s subsystem : %d", uh->subsystem, r);
246                         return -EPERM;
247                 }
248         }
249
250         r = udev_monitor_filter_update(info->mon);
251         if (r < 0)
252                 _E("fail to update udev monitor filter : %d", r);
253
254 add_list:
255         info->event_list = g_list_append(info->event_list, uh);
256         return 0;
257 }
258
259 static int unregister_uevent_control(struct uevent_info *info,
260                 const struct uevent_handler *uh)
261 {
262         struct uevent_handler *l;
263         GList *n, *next;
264         int len;
265
266         if (!info || !uh || !uh->subsystem)
267                 return -EINVAL;
268
269         len = strlen(uh->subsystem);
270         for (n = info->event_list, next = g_list_next(n) ;
271                         n ; n = next, next = g_list_next(n)) {
272                 l = n->data;
273                 if (!strncmp(l->subsystem, uh->subsystem, len) &&
274                     l->uevent_func == uh->uevent_func) {
275                         info->event_list = g_list_delete_link(info->event_list, n);
276                         return 0;
277                 }
278         }
279
280         return -ENOENT;
281 }
282
283 int register_kernel_event_control(struct uevent_handler *uh)
284 {
285         return register_uevent_control(&kevent, uh);
286 }
287
288 void unregister_kernel_event_control(struct uevent_handler *uh)
289 {
290         unregister_uevent_control(&kevent, uh);
291 }
292
293 int register_udev_event_control(struct uevent_handler *uh)
294 {
295         return register_uevent_control(&uevent, uh);
296 }
297
298 void unregister_udev_event_control(struct uevent_handler *uh)
299 {
300         unregister_uevent_control(&uevent, uh);
301 }