f4b925572db7fd92c497394738cd9a30953547a4
[framework/connectivity/connman.git] / src / udev.c
1 /*
2  *
3  *  Connection Manager
4  *
5  *  Copyright (C) 2007-2009  Intel Corporation. All rights reserved.
6  *
7  *  This program is free software; you can redistribute it and/or modify
8  *  it under the terms of the GNU General Public License version 2 as
9  *  published by the Free Software Foundation.
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  *  You should have received a copy of the GNU General Public License
17  *  along with this program; if not, write to the Free Software
18  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
19  *
20  */
21
22 #ifdef HAVE_CONFIG_H
23 #include <config.h>
24 #endif
25
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <sys/types.h>
29
30 #define LIBUDEV_I_KNOW_THE_API_IS_SUBJECT_TO_CHANGE
31 #include <libudev.h>
32
33 #include <glib.h>
34
35 #include "connman.h"
36
37 #ifdef NEED_UDEV_MONITOR_FILTER
38 static int udev_monitor_filter_add_match_subsystem_devtype(struct udev_monitor *udev_monitor,
39                                 const char *subsystem, const char *devtype)
40 {
41         return -EINVAL;
42 }
43 static int udev_monitor_filter_update(struct udev_monitor *udev_monitor)
44 {
45         return -EINVAL;
46 }
47 static int udev_monitor_filter_remove(struct udev_monitor *udev_monitor)
48 {
49         return -EINVAL;
50 }
51 #endif
52
53 static gboolean rfkill_processing = FALSE;
54
55 static GSList *device_list = NULL;
56
57 static struct connman_device *find_device(int index)
58 {
59         GSList *list;
60
61         if (index < 0)
62                 return NULL;
63
64         for (list = device_list; list; list = list->next) {
65                 struct connman_device *device = list->data;
66
67                 if (connman_device_get_index(device) == index)
68                         return device;
69         }
70
71         return NULL;
72 }
73
74 static void add_net_device(struct udev_device *udev_device)
75 {
76         struct udev_list_entry *entry;
77         struct connman_device *device;
78         enum connman_device_type devtype;
79         const char *value, *systype;
80         int index = -1;
81
82         DBG("");
83
84         systype = udev_device_get_sysattr_value(udev_device, "type");
85         if (systype == NULL || atoi(systype) != 1)
86                 return;
87
88         entry = udev_device_get_properties_list_entry(udev_device);
89         while (entry) {
90                 const char *name = udev_list_entry_get_name(entry);
91
92                 if (g_str_has_prefix(name, "IFINDEX") == TRUE) {
93                         const char *value = udev_list_entry_get_value(entry);
94                         if (value != NULL)
95                                 index = atoi(value);
96                 }
97
98                 entry = udev_list_entry_get_next(entry);
99         }
100
101         if (index < 0)
102                 return;
103
104         devtype = __connman_inet_get_device_type(index);
105
106         switch (devtype) {
107         case CONNMAN_DEVICE_TYPE_UNKNOWN:
108         case CONNMAN_DEVICE_TYPE_VENDOR:
109         case CONNMAN_DEVICE_TYPE_WIMAX:
110         case CONNMAN_DEVICE_TYPE_BLUETOOTH:
111         case CONNMAN_DEVICE_TYPE_GPS:
112         case CONNMAN_DEVICE_TYPE_NOZOMI:
113         case CONNMAN_DEVICE_TYPE_HUAWEI:
114         case CONNMAN_DEVICE_TYPE_NOVATEL:
115                 return;
116         case CONNMAN_DEVICE_TYPE_ETHERNET:
117         case CONNMAN_DEVICE_TYPE_WIFI:
118         case CONNMAN_DEVICE_TYPE_MBM:
119         case CONNMAN_DEVICE_TYPE_HSO:
120                 break;
121         }
122
123         device = find_device(index);
124         if (device != NULL)
125                 return;
126
127         device = connman_inet_create_device(index);
128         if (device == NULL)
129                 return;
130
131         value = udev_device_get_sysattr_value(udev_device, "phy80211/index");
132         if (value != NULL)
133                 __connman_device_set_phyindex(device, atoi(value));
134
135         if (connman_device_register(device) < 0) {
136                 connman_device_unref(device);
137                 return;
138         }
139
140         device_list = g_slist_append(device_list, device);
141 }
142
143 static void remove_net_device(struct udev_device *udev_device)
144 {
145         struct udev_list_entry *entry;
146         struct connman_device *device;
147         int index = -1;
148
149         DBG("");
150
151         entry = udev_device_get_properties_list_entry(udev_device);
152         while (entry) {
153                 const char *name = udev_list_entry_get_name(entry);
154
155                 if (g_str_has_prefix(name, "IFINDEX") == TRUE) {
156                         const char *value = udev_list_entry_get_value(entry);
157                         if (value != NULL)
158                                 index = atoi(value);
159                 }
160
161                 entry = udev_list_entry_get_next(entry);
162         }
163
164         if (index < 0)
165                 return;
166
167         device = find_device(index);
168         if (device == NULL)
169                 return;
170
171         device_list = g_slist_remove(device_list, device);
172
173         connman_device_unregister(device);
174         connman_device_unref(device);
175 }
176
177 static void phyindex_rfkill(int phyindex, connman_bool_t blocked)
178 {
179         GSList *list;
180
181         if (phyindex < 0)
182                 return;
183
184         for (list = device_list; list; list = list->next) {
185                 struct connman_device *device = list->data;
186
187                 if (__connman_device_get_phyindex(device) == phyindex)
188                         __connman_device_set_blocked(device, blocked);
189         }
190 }
191
192 static void change_rfkill_device(struct udev_device *device)
193 {
194         struct udev_device *parent;
195         struct udev_list_entry *entry;
196         connman_bool_t blocked;
197         const char *value, *type = NULL;
198         int state = -1;
199
200         if (rfkill_processing == FALSE)
201                 return;
202
203         entry = udev_device_get_properties_list_entry(device);
204         while (entry) {
205                 const char *name = udev_list_entry_get_name(entry);
206
207                 if (g_str_has_prefix(name, "RFKILL_STATE") == TRUE) {
208                         value = udev_list_entry_get_value(entry);
209                         if (value != NULL)
210                                 state = atoi(value);
211                 } else if (g_str_has_prefix(name, "RFKILL_TYPE") == TRUE)
212                         type = udev_list_entry_get_value(entry);
213
214                 entry = udev_list_entry_get_next(entry);
215         }
216
217         if (type == NULL || state < 0)
218                 return;
219
220         if (g_str_equal(type, "wlan") == FALSE)
221                 return;
222
223         parent = udev_device_get_parent(device);
224         if (parent == NULL)
225                 return;
226
227         value = udev_device_get_sysattr_value(parent, "index");
228         if (value == NULL)
229                 return;
230
231         blocked = (state != 1) ? TRUE : FALSE;
232
233         phyindex_rfkill(atoi(value), blocked);
234 }
235
236 static void add_rfkill_device(struct udev_device *device)
237 {
238         change_rfkill_device(device);
239 }
240
241 static void print_properties(struct udev_device *device, const char *prefix)
242 {
243         struct udev_list_entry *entry;
244
245         entry = udev_device_get_properties_list_entry(device);
246         while (entry) {
247                 const char *name = udev_list_entry_get_name(entry);
248                 const char *value = udev_list_entry_get_value(entry);
249
250                 if (g_str_has_prefix(name, "CONNMAN") == TRUE ||
251                                 g_str_has_prefix(name, "RFKILL") == TRUE ||
252                                 g_str_has_prefix(name, "ID_MODEM") == TRUE ||
253                                 g_str_equal(name, "ID_VENDOR") == TRUE ||
254                                 g_str_equal(name, "ID_MODEL") == TRUE ||
255                                 g_str_equal(name, "INTERFACE") == TRUE ||
256                                 g_str_equal(name, "IFINDEX") == TRUE ||
257                                 g_str_equal(name, "DEVNAME") == TRUE ||
258                                 g_str_equal(name, "DEVPATH") == TRUE)
259                         connman_debug("%s%s = %s", prefix, name, value);
260
261                 entry = udev_list_entry_get_next(entry);
262         }
263 }
264
265 static void print_device(struct udev_device *device, const char *action)
266 {
267         const char *subsystem, *sysname, *driver, *devtype = NULL;
268         struct udev_device *parent;
269
270         connman_debug("=== %s ===", action);
271         print_properties(device, "");
272
273         parent = udev_device_get_parent(device);
274         if (parent == NULL)
275                 return;
276
277         subsystem = udev_device_get_subsystem(parent);
278
279         if (subsystem != NULL &&
280                         g_str_equal(subsystem, "usb-serial") == TRUE) {
281                 subsystem = "usb";
282                 devtype = "usb_device";
283         }
284
285         parent = udev_device_get_parent_with_subsystem_devtype(device,
286                                                         subsystem, devtype);
287         print_properties(parent, "    ");
288
289         driver = udev_device_get_driver(device);
290         if (driver == NULL) {
291                 driver = udev_device_get_driver(parent);
292                 if (driver == NULL)
293                         return;
294         }
295
296         devtype = udev_device_get_devtype(device);
297         sysname = udev_device_get_sysname(device);
298
299         driver = udev_device_get_driver(parent);
300 }
301
302 static void enumerate_devices(struct udev *context)
303 {
304         struct udev_enumerate *enumerate;
305         struct udev_list_entry *entry;
306
307         enumerate = udev_enumerate_new(context);
308         if (enumerate == NULL)
309                 return;
310
311         udev_enumerate_add_match_subsystem(enumerate, "net");
312         udev_enumerate_add_match_subsystem(enumerate, "rfkill");
313
314         udev_enumerate_scan_devices(enumerate);
315
316         entry = udev_enumerate_get_list_entry(enumerate);
317         while (entry) {
318                 const char *syspath = udev_list_entry_get_name(entry);
319                 struct udev_device *device;
320
321                 device = udev_device_new_from_syspath(context, syspath);
322                 if (device != NULL) {
323                         const char *subsystem;
324
325                         print_device(device, "coldplug");
326
327                         subsystem = udev_device_get_subsystem(device);
328
329                         if (g_strcmp0(subsystem, "net") == 0)
330                                 add_net_device(device);
331                         else if (g_strcmp0(subsystem, "rfkill") == 0)
332                                 add_rfkill_device(device);
333
334                         udev_device_unref(device);
335                 }
336
337                 entry = udev_list_entry_get_next(entry);
338         }
339
340         udev_enumerate_unref(enumerate);
341 }
342
343 static gboolean udev_event(GIOChannel *channel,
344                                 GIOCondition condition, gpointer user_data)
345 {
346         struct udev_monitor *monitor = user_data;
347         struct udev_device *device;
348         const char *subsystem, *action;
349
350         device = udev_monitor_receive_device(monitor);
351         if (device == NULL)
352                 return TRUE;
353
354         subsystem = udev_device_get_subsystem(device);
355         if (subsystem == NULL)
356                 goto done;
357
358         action = udev_device_get_action(device);
359         if (action == NULL)
360                 goto done;
361
362         print_device(device, action);
363
364         if (g_str_equal(action, "add") == TRUE) {
365                 if (g_str_equal(subsystem, "net") == TRUE)
366                         add_net_device(device);
367                 else if (g_str_equal(subsystem, "rfkill") == TRUE)
368                         add_rfkill_device(device);
369         } else if (g_str_equal(action, "remove") == TRUE) {
370                 if (g_str_equal(subsystem, "net") == TRUE)
371                         remove_net_device(device);
372         } else if (g_str_equal(action, "change") == TRUE) {
373                 if (g_str_equal(subsystem, "rfkill") == TRUE)
374                         change_rfkill_device(device);
375         }
376
377 done:
378         udev_device_unref(device);
379
380         return TRUE;
381 }
382
383 static struct udev *udev_ctx;
384 static struct udev_monitor *udev_mon;
385 static guint udev_watch = 0;
386
387 void __connman_udev_enable_rfkill_processing(void)
388 {
389         rfkill_processing = TRUE;
390
391         enumerate_devices(udev_ctx);
392 }
393
394 char *__connman_udev_get_devtype(const char *ifname)
395 {
396         struct udev_device *device;
397         const char *devtype;
398
399         device = udev_device_new_from_subsystem_sysname(udev_ctx,
400                                                         "net", ifname);
401         if (device == NULL)
402                 return NULL;
403
404         devtype = udev_device_get_devtype(device);
405         if (devtype == NULL)
406                 goto done;
407
408 done:
409         udev_device_unref(device);
410
411         return NULL;
412 }
413
414 char *__connman_udev_get_mbm_devnode(const char *ifname)
415 {
416         struct udev_device *device, *parent, *control;
417         const char *driver, *devpath1, *devpath2;
418         char *devnode = NULL;
419         unsigned int i;
420
421         device = udev_device_new_from_subsystem_sysname(udev_ctx,
422                                                         "net", ifname);
423         if (device == NULL)
424                 return NULL;
425
426         parent = udev_device_get_parent(device);
427         if (parent == NULL)
428                 goto done;
429
430         driver = udev_device_get_driver(parent);
431         if (g_strcmp0(driver, "cdc_ether") != 0)
432                 goto done;
433
434         parent = udev_device_get_parent_with_subsystem_devtype(device,
435                                                         "usb", "usb_device");
436         if (parent == NULL)
437                 goto done;
438
439         devpath1 = udev_device_get_devpath(parent);
440
441         for (i = 0; i < 64; i++) {
442                 char sysname[16];
443
444                 snprintf(sysname, sizeof(sysname) - 1, "ttyACM%d", i);
445
446                 control = udev_device_new_from_subsystem_sysname(udev_ctx,
447                                                         "tty", sysname);
448                 if (control == NULL)
449                         continue;
450
451                 parent = udev_device_get_parent_with_subsystem_devtype(control,
452                                                         "usb", "usb_device");
453                 if (parent == NULL)
454                         continue;
455
456                 devpath2 = udev_device_get_devpath(parent);
457
458                 if (g_strcmp0(devpath1, devpath2) == 0) {
459                         devnode = g_strdup(udev_device_get_devnode(control));
460                         break;
461                 }
462         }
463
464 done:
465         udev_device_unref(device);
466
467         return devnode;
468 }
469
470 void __connman_udev_rfkill(const char *sysname, connman_bool_t blocked)
471 {
472         struct udev_device *device, *parent;
473         const char *value;
474
475         device = udev_device_new_from_subsystem_sysname(udev_ctx,
476                                                         "rfkill", sysname);
477         if (device == NULL)
478                 return;
479
480         parent = udev_device_get_parent(device);
481         if (parent == NULL)
482                 return;
483
484         value = udev_device_get_sysattr_value(parent, "index");
485         if (value == NULL)
486                 return;
487
488         phyindex_rfkill(atoi(value), blocked);
489 }
490
491 int __connman_udev_init(void)
492 {
493         DBG("");
494
495         udev_ctx = udev_new();
496         if (udev_ctx == NULL) {
497                 connman_error("Failed to create udev context");
498                 return -1;
499         }
500
501         udev_mon = udev_monitor_new_from_netlink(udev_ctx, "udev");
502         if (udev_mon == NULL) {
503                 connman_error("Failed to create udev monitor");
504                 udev_unref(udev_ctx);
505                 udev_ctx = NULL;
506                 return -1;
507         }
508
509         udev_monitor_filter_add_match_subsystem_devtype(udev_mon,
510                                                         "net", NULL);
511         udev_monitor_filter_add_match_subsystem_devtype(udev_mon,
512                                                         "rfkill", NULL);
513
514         udev_monitor_filter_update(udev_mon);
515
516         return 0;
517 }
518
519 void __connman_udev_start(void)
520 {
521         GIOChannel *channel;
522         int fd;
523
524         DBG("");
525
526         if (udev_monitor_enable_receiving(udev_mon) < 0) {
527                 connman_error("Failed to enable udev monitor");
528                 return;
529         }
530
531         enumerate_devices(udev_ctx);
532
533         fd = udev_monitor_get_fd(udev_mon);
534
535         channel = g_io_channel_unix_new(fd);
536         if (channel == NULL)
537                 return;
538
539         udev_watch = g_io_add_watch(channel, G_IO_IN, udev_event, udev_mon);
540
541         g_io_channel_unref(channel);
542 }
543
544 void __connman_udev_cleanup(void)
545 {
546         GSList *list;
547
548         DBG("");
549
550         if (udev_watch > 0)
551                 g_source_remove(udev_watch);
552
553         for (list = device_list; list; list = list->next) {
554                 struct connman_device *device = list->data;
555
556                 connman_device_unregister(device);
557                 connman_device_unref(device);
558         }
559
560         g_slist_free(device_list);
561         device_list = NULL;
562
563         if (udev_ctx == NULL)
564                 return;
565
566         udev_monitor_filter_remove(udev_mon);
567
568         udev_monitor_unref(udev_mon);
569         udev_unref(udev_ctx);
570 }