9067126d2d1025dda012b0b3003af5a076165366
[platform/adaptation/RPI3/device-manager-plugin-RPI3.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 #include <hw/shared.h>
27 #include "udev.h"
28
29 #define EVENT_KERNEL       "kernel"
30 #define EVENT_UDEV         "udev"
31
32 #define UDEV_MONITOR_SIZE   (128*1024)
33
34 struct uevent_info {
35         struct udev_monitor *mon;
36         GIOChannel *ch;
37         guint eventid;
38         GList *event_list;
39 };
40
41
42 /* Uevent */
43 static struct udev *udev;
44 static struct uevent_info kevent; /* kernel */
45 static struct uevent_info uevent; /* udev */
46
47 static gboolean uevent_control_cb(GIOChannel *channel,
48                 GIOCondition cond, void *data)
49 {
50         struct uevent_info *info = data;
51         struct udev_device *dev;
52         struct uevent_handler *l;
53         GList *elem;
54         const char *subsystem;
55         int len;
56
57         if (!info) {
58                 _E("data is invalid");
59                 return TRUE;
60         }
61
62         _I("Set udev monitor buffer size %d", UDEV_MONITOR_SIZE);
63         dev = udev_monitor_receive_device(info->mon);
64         if (!dev)
65                 return TRUE;
66
67         subsystem = udev_device_get_subsystem(dev);
68         if (!subsystem)
69                 goto out;
70
71         len = strlen(subsystem);
72
73         for (elem = info->event_list ; elem ; elem = g_list_next(elem)) {
74                 l = elem->data;
75                 if (!l)
76                         continue;
77                 if (!strncmp(l->subsystem, subsystem, len) &&
78                     l->uevent_func)
79                         l->uevent_func(dev);
80         }
81
82 out:
83         udev_device_unref(dev);
84         return TRUE;
85 }
86
87 static int uevent_control_stop(struct uevent_info *info)
88 {
89         struct udev_device *dev;
90
91         if (!info)
92                 return -EINVAL;
93
94         if (info->eventid) {
95                 g_source_remove(info->eventid);
96                 info->eventid = 0;
97         }
98         if (info->ch) {
99                 g_io_channel_unref(info->ch);
100                 info->ch = NULL;
101         }
102         if (info->mon) {
103                 dev = udev_monitor_receive_device(info->mon);
104                 if (dev)
105                         udev_device_unref(dev);
106                 udev_monitor_unref(info->mon);
107                 info->mon = NULL;
108         }
109         if (udev)
110                 udev = udev_unref(udev);
111         return 0;
112 }
113
114 static int uevent_control_start(const char *type,
115                 struct uevent_info *info)
116 {
117         struct uevent_handler *l;
118         GList *elem;
119         int fd;
120         int ret;
121
122         if (!info)
123                 return -EINVAL;
124
125         if (info->mon) {
126                 _E("%s uevent control routine is alreay started", type);
127                 return -EINVAL;
128         }
129
130         if (!udev) {
131                 udev = udev_new();
132                 if (!udev) {
133                         _E("error create udev");
134                         return -EINVAL;
135                 }
136         } else
137                 udev = udev_ref(udev);
138
139         info->mon = udev_monitor_new_from_netlink(udev, type);
140         if (info->mon == NULL) {
141                 _E("error udev_monitor create");
142                 goto stop;
143         }
144
145         ret = udev_monitor_set_receive_buffer_size(info->mon,
146                         UDEV_MONITOR_SIZE);
147         if (ret != 0) {
148                 _E("fail to set receive buffer size");
149                 goto stop;
150         }
151
152         for (elem = info->event_list ; elem ; elem = g_list_next(elem)) {
153                 l = elem->data;
154                 ret = udev_monitor_filter_add_match_subsystem_devtype(
155                                 info->mon,
156                                 l->subsystem, NULL);
157                 if (ret < 0) {
158                         _E("error apply subsystem filter");
159                         goto stop;
160                 }
161         }
162
163         ret = udev_monitor_filter_update(info->mon);
164         if (ret < 0)
165                 _E("error udev_monitor_filter_update");
166
167         fd = udev_monitor_get_fd(info->mon);
168         if (fd == -1) {
169                 _E("error udev_monitor_get_fd");
170                 goto stop;
171         }
172
173         info->ch = g_io_channel_unix_new(fd);
174         info->eventid = g_io_add_watch(info->ch,
175                         G_IO_IN, uevent_control_cb, info);
176         if (info->eventid == 0) {
177                 _E("Failed to add channel watch");
178                 goto stop;
179         }
180
181         if (udev_monitor_enable_receiving(info->mon) < 0) {
182                 _E("error unable to subscribe to udev events");
183                 goto stop;
184         }
185
186         return 0;
187 stop:
188         uevent_control_stop(info);
189         return -EINVAL;
190 }
191
192 int uevent_control_kernel_start(void)
193 {
194         return uevent_control_start(EVENT_KERNEL, &kevent);
195 }
196
197 void uevent_control_kernel_stop(void)
198 {
199         uevent_control_stop(&kevent);
200 }
201
202 int uevent_control_udev_start(void)
203 {
204         return uevent_control_start(EVENT_UDEV, &uevent);
205 }
206
207 void uevent_control_udev_stop(void)
208 {
209         uevent_control_stop(&uevent);
210 }
211
212 static int register_uevent_control(struct uevent_info *info,
213                 struct uevent_handler *uh)
214 {
215         struct uevent_handler *l;
216         GList *elem;
217         int r;
218         bool matched = false;
219         int len;
220
221         if (!info || !uh || !uh->subsystem)
222                 return -EINVAL;
223
224         /* if udev is not initialized, it just will be added list */
225         if (!udev || !info->mon)
226                 goto add_list;
227
228         len = strlen(uh->subsystem);
229         /* check if the same subsystem is already added */
230         for (elem = info->event_list; elem ; elem = g_list_next(elem)) {
231                 l = elem->data;
232                 if (!strncmp(l->subsystem, uh->subsystem, len)) {
233                         matched = true;
234                         break;
235                 }
236         }
237
238         /* the first request to add subsystem */
239         if (!matched) {
240                 r = udev_monitor_filter_add_match_subsystem_devtype(info->mon,
241                                 uh->subsystem, NULL);
242                 if (r < 0) {
243                         _E("fail to add %s subsystem : %d", uh->subsystem, r);
244                         return -EPERM;
245                 }
246         }
247
248         r = udev_monitor_filter_update(info->mon);
249         if (r < 0)
250                 _E("fail to update udev monitor filter : %d", r);
251
252 add_list:
253         info->event_list = g_list_append(info->event_list, uh);
254         return 0;
255 }
256
257 static int unregister_uevent_control(struct uevent_info *info,
258                 const struct uevent_handler *uh)
259 {
260         struct uevent_handler *l;
261         GList *n, *next;
262         int len;
263
264         if (!info || !uh || !uh->subsystem)
265                 return -EINVAL;
266
267         len = strlen(uh->subsystem);
268         for (n = info->event_list, next = g_list_next(n) ;
269                         n ; n = next, next = g_list_next(n)) {
270                 l = n->data;
271                 if (!strncmp(l->subsystem, uh->subsystem, len) &&
272                     l->uevent_func == uh->uevent_func) {
273                         info->event_list = g_list_delete_link(info->event_list, n);
274                         return 0;
275                 }
276         }
277
278         return -ENOENT;
279 }
280
281 int register_kernel_event_control(struct uevent_handler *uh)
282 {
283         return register_uevent_control(&kevent, uh);
284 }
285
286 void unregister_kernel_event_control(struct uevent_handler *uh)
287 {
288         unregister_uevent_control(&kevent, uh);
289 }
290
291 int register_udev_event_control(struct uevent_handler *uh)
292 {
293         return register_uevent_control(&uevent, uh);
294 }
295
296 void unregister_udev_event_control(struct uevent_handler *uh)
297 {
298         unregister_uevent_control(&uevent, uh);
299 }