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