Upgrade ofono to 1.2
[profile/ivi/ofono.git] / plugins / udev.c
1 /*
2  *
3  *  oFono - Open Source Telephony
4  *
5  *  Copyright (C) 2008-2011  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 <errno.h>
27 #include <ctype.h>
28 #include <stdlib.h>
29
30 #include <libudev.h>
31
32 #include <glib.h>
33 #include <string.h>
34
35 #define OFONO_API_SUBJECT_TO_CHANGE
36 #include <ofono/plugin.h>
37 #include <ofono/modem.h>
38 #include <ofono/log.h>
39
40 static GSList *modem_list = NULL;
41 static GHashTable *devpath_list = NULL;
42
43 static struct ofono_modem *find_modem(const char *devpath)
44 {
45         GSList *list;
46
47         for (list = modem_list; list; list = list->next) {
48                 struct ofono_modem *modem = list->data;
49                 const char *path = ofono_modem_get_string(modem, "Path");
50
51                 if (g_strcmp0(devpath, path) == 0)
52                         return modem;
53         }
54
55         return NULL;
56 }
57
58 static const char *get_property(struct udev_device *device,
59                                 char const *property_name)
60 {
61         struct udev_list_entry *entry;
62
63         entry = udev_device_get_properties_list_entry(device);
64         while (entry) {
65                 const char *name = udev_list_entry_get_name(entry);
66
67                 if (g_strcmp0(name, property_name) == 0)
68                         return udev_list_entry_get_value(entry);
69
70                 entry = udev_list_entry_get_next(entry);
71         }
72
73         return NULL;
74 }
75
76 static const char *get_driver(struct udev_device *udev_device)
77 {
78         return get_property(udev_device, "OFONO_DRIVER");
79 }
80
81 static const char *get_serial(struct udev_device *udev_device)
82 {
83         const char *serial;
84
85         serial = get_property(udev_device, "ID_SERIAL_SHORT");
86
87         if (serial != NULL) {
88                 unsigned int i, len = strlen(serial);
89
90                 for (i = 0; i < len; i++) {
91                         if (!g_ascii_isalnum(serial[i]))
92                                 return NULL;
93                 }
94         }
95
96         return serial;
97 }
98
99 static void add_ifx(struct ofono_modem *modem,
100                                         struct udev_device *udev_device)
101 {
102         struct udev_list_entry *entry;
103         const char *devnode;
104
105         DBG("modem %p", modem);
106
107         devnode = udev_device_get_devnode(udev_device);
108         ofono_modem_set_string(modem, "Device", devnode);
109
110         entry = udev_device_get_properties_list_entry(udev_device);
111         while (entry) {
112                 const char *name = udev_list_entry_get_name(entry);
113                 const char *value = udev_list_entry_get_value(entry);
114
115                 if (g_str_equal(name, "OFONO_IFX_LDISC") == TRUE)
116                         ofono_modem_set_string(modem, "LineDiscipline", value);
117                 else if (g_str_equal(name, "OFONO_IFX_AUDIO") == TRUE)
118                         ofono_modem_set_string(modem, "AudioSetting", value);
119                 else if (g_str_equal(name, "OFONO_IFX_LOOPBACK") == TRUE)
120                         ofono_modem_set_string(modem, "AudioLoopback", value);
121
122                 entry = udev_list_entry_get_next(entry);
123         }
124
125         ofono_modem_register(modem);
126 }
127
128 static void add_isi(struct ofono_modem *modem,
129                                         struct udev_device *udev_device)
130 {
131         const char *ifname, *type, *addr;
132
133         DBG("modem %p", modem);
134
135         if (ofono_modem_get_string(modem, "Interface"))
136                 return;
137
138         addr = get_property(udev_device, "OFONO_ISI_ADDRESS");
139         if (addr != NULL)
140                 ofono_modem_set_integer(modem, "Address", atoi(addr));
141
142         if (g_strcmp0(udev_device_get_subsystem(udev_device), "net") != 0)
143                 return;
144
145         type = udev_device_get_sysattr_value(udev_device, "type");
146         if (g_strcmp0(type, "820") != 0)
147                 return;
148
149         ifname = udev_device_get_sysname(udev_device);
150         ofono_modem_set_string(modem, "Interface", ifname);
151
152         DBG("interface %s", ifname);
153
154         ofono_modem_register(modem);
155 }
156
157 static void add_calypso(struct ofono_modem *modem,
158                                         struct udev_device *udev_device)
159 {
160         const char *devnode;
161
162         DBG("modem %p", modem);
163
164         devnode = udev_device_get_devnode(udev_device);
165         ofono_modem_set_string(modem, "Device", devnode);
166
167         ofono_modem_register(modem);
168 }
169
170 static void add_wavecom(struct ofono_modem *modem,
171                                         struct udev_device *udev_device)
172 {
173         const char *devnode;
174         struct udev_list_entry *entry;
175
176         DBG("modem %p", modem);
177
178         devnode = udev_device_get_devnode(udev_device);
179         ofono_modem_set_string(modem, "Device", devnode);
180
181         entry = udev_device_get_properties_list_entry(udev_device);
182         while (entry) {
183                 const char *name = udev_list_entry_get_name(entry);
184                 const char *value = udev_list_entry_get_value(entry);
185
186                 if (g_str_equal(name, "OFONO_WAVECOM_MODEL") == TRUE)
187                         ofono_modem_set_string(modem, "Model", value);
188
189                 entry = udev_list_entry_get_next(entry);
190         }
191
192         ofono_modem_register(modem);
193 }
194
195 static void add_tc65(struct ofono_modem *modem,
196                         struct udev_device *udev_device)
197 {
198         const char *devnode;
199
200         DBG("modem %p", modem);
201
202         devnode = udev_device_get_devnode(udev_device);
203         ofono_modem_set_string(modem, "Device", devnode);
204
205         ofono_modem_register(modem);
206 }
207
208 static void add_nokiacdma(struct ofono_modem *modem,
209                                         struct udev_device *udev_device)
210 {
211         const char *devnode;
212
213         DBG("modem %p", modem);
214
215         devnode = udev_device_get_devnode(udev_device);
216         ofono_modem_set_string(modem, "Device", devnode);
217
218         ofono_modem_register(modem);
219 }
220
221 static void add_sim900(struct ofono_modem *modem,
222                                         struct udev_device *udev_device)
223 {
224         const char *devnode;
225
226         DBG("modem %p", modem);
227
228         devnode = udev_device_get_devnode(udev_device);
229         ofono_modem_set_string(modem, "Device", devnode);
230
231         ofono_modem_register(modem);
232 }
233
234 static void add_modem(struct udev_device *udev_device)
235 {
236         struct ofono_modem *modem;
237         struct udev_device *parent;
238         const char *devpath, *curpath, *driver;
239
240         driver = get_driver(udev_device);
241         if (driver != NULL) {
242                 devpath = udev_device_get_devpath(udev_device);
243                 if (devpath == NULL)
244                         return;
245
246                 modem = ofono_modem_create(NULL, driver);
247                 if (modem == NULL)
248                         return;
249
250                 ofono_modem_set_string(modem, "Path", devpath);
251
252                 modem_list = g_slist_prepend(modem_list, modem);
253
254                 goto done;
255         }
256
257         parent = udev_device_get_parent(udev_device);
258         if (parent == NULL)
259                 return;
260
261         driver = get_driver(parent);
262         if (driver == NULL) {
263                 parent = udev_device_get_parent(parent);
264                 driver = get_driver(parent);
265                 if (driver == NULL) {
266                         parent = udev_device_get_parent(parent);
267                         driver = get_driver(parent);
268                         if (driver == NULL)
269                                 return;
270                 }
271         }
272
273         devpath = udev_device_get_devpath(parent);
274         if (devpath == NULL)
275                 return;
276
277         modem = find_modem(devpath);
278         if (modem == NULL) {
279                 const char *serial = get_serial(parent);
280
281                 modem = ofono_modem_create(serial, driver);
282                 if (modem == NULL)
283                         return;
284
285                 ofono_modem_set_string(modem, "Path", devpath);
286                 ofono_modem_set_integer(modem, "Registered", 0);
287
288                 modem_list = g_slist_prepend(modem_list, modem);
289         }
290
291 done:
292         curpath = udev_device_get_devpath(udev_device);
293         if (curpath == NULL)
294                 return;
295
296         DBG("%s (%s)", curpath, driver);
297
298         g_hash_table_insert(devpath_list, g_strdup(curpath), g_strdup(devpath));
299
300         if (g_strcmp0(driver, "ifx") == 0)
301                 add_ifx(modem, udev_device);
302         else if (g_strcmp0(driver, "u8500") == 0)
303                 add_isi(modem, udev_device);
304         else if (g_strcmp0(driver, "n900") == 0)
305                 add_isi(modem, udev_device);
306         else if (g_strcmp0(driver, "calypso") == 0)
307                 add_calypso(modem, udev_device);
308         else if (g_strcmp0(driver, "tc65") == 0)
309                 add_tc65(modem, udev_device);
310         else if (g_strcmp0(driver, "nokiacdma") == 0)
311                 add_nokiacdma(modem, udev_device);
312         else if (g_strcmp0(driver, "sim900") == 0)
313                 add_sim900(modem, udev_device);
314         else if (g_strcmp0(driver, "wavecom") == 0)
315                 add_wavecom(modem, udev_device);
316 }
317
318 static gboolean devpath_remove(gpointer key, gpointer value, gpointer user_data)
319 {
320         const char *path = value;
321         const char *devpath = user_data;
322
323         DBG("%s -> %s", path, devpath);
324
325         return g_str_equal(path, devpath);
326 }
327
328 static void remove_modem(struct udev_device *udev_device)
329 {
330         struct ofono_modem *modem;
331         const char *curpath = udev_device_get_devpath(udev_device);
332         char *devpath, *remove;
333
334         if (curpath == NULL)
335                 return;
336
337         DBG("%s", curpath);
338
339         devpath = g_hash_table_lookup(devpath_list, curpath);
340         if (devpath == NULL)
341                 return;
342
343         modem = find_modem(devpath);
344         if (modem == NULL)
345                 return;
346
347         modem_list = g_slist_remove(modem_list, modem);
348
349         ofono_modem_remove(modem);
350
351         DBG("%s", devpath);
352
353         remove = g_strdup(devpath);
354
355         g_hash_table_foreach_remove(devpath_list, devpath_remove, remove);
356
357         g_free(remove);
358 }
359
360 static void enumerate_devices(struct udev *context)
361 {
362         struct udev_enumerate *enumerate;
363         struct udev_list_entry *entry;
364
365         enumerate = udev_enumerate_new(context);
366         if (enumerate == NULL)
367                 return;
368
369         udev_enumerate_add_match_subsystem(enumerate, "tty");
370         udev_enumerate_add_match_subsystem(enumerate, "net");
371         udev_enumerate_add_match_subsystem(enumerate, "hsi");
372
373         udev_enumerate_scan_devices(enumerate);
374
375         entry = udev_enumerate_get_list_entry(enumerate);
376         while (entry) {
377                 const char *syspath = udev_list_entry_get_name(entry);
378                 struct udev_device *device;
379
380                 device = udev_device_new_from_syspath(context, syspath);
381                 if (device != NULL) {
382                         const char *subsystem;
383
384                         subsystem = udev_device_get_subsystem(device);
385
386                         if (g_strcmp0(subsystem, "tty") == 0 ||
387                                         g_strcmp0(subsystem, "net") == 0 ||
388                                         g_strcmp0(subsystem, "hsi") == 0)
389                                 add_modem(device);
390
391                         udev_device_unref(device);
392                 }
393
394                 entry = udev_list_entry_get_next(entry);
395         }
396
397         udev_enumerate_unref(enumerate);
398 }
399
400 static struct udev *udev_ctx;
401 static struct udev_monitor *udev_mon;
402 static guint udev_watch = 0;
403
404 static gboolean udev_event(GIOChannel *channel, GIOCondition cond,
405                                                         gpointer user_data)
406 {
407         struct udev_device *device;
408         const char *subsystem, *action;
409
410         if (cond & (G_IO_ERR | G_IO_HUP | G_IO_NVAL)) {
411                 ofono_warn("Error with udev monitor channel");
412                 udev_watch = 0;
413                 return FALSE;
414         }
415
416         device = udev_monitor_receive_device(udev_mon);
417         if (device == NULL)
418                 return TRUE;
419
420         subsystem = udev_device_get_subsystem(device);
421         if (subsystem == NULL)
422                 goto done;
423
424         action = udev_device_get_action(device);
425         if (action == NULL)
426                 goto done;
427
428         DBG("subsystem %s %s", subsystem, action);
429
430         if (g_str_equal(action, "add") == TRUE) {
431                 if (g_strcmp0(subsystem, "tty") == 0 ||
432                                 g_strcmp0(subsystem, "net") == 0 ||
433                                         g_strcmp0(subsystem, "hsi") == 0)
434                         add_modem(device);
435         } else if (g_str_equal(action, "remove") == TRUE) {
436                 if (g_strcmp0(subsystem, "tty") == 0 ||
437                                 g_strcmp0(subsystem, "net") == 0 ||
438                                         g_strcmp0(subsystem, "hsi") == 0)
439                         remove_modem(device);
440         }
441
442         DBG("subsystem %s finished", subsystem);
443
444 done:
445         udev_device_unref(device);
446
447         return TRUE;
448 }
449
450 static void udev_start(void)
451 {
452         GIOChannel *channel;
453         int fd;
454
455         if (udev_monitor_enable_receiving(udev_mon) < 0) {
456                 ofono_error("Failed to enable udev monitor");
457                 return;
458         }
459
460         enumerate_devices(udev_ctx);
461
462         fd = udev_monitor_get_fd(udev_mon);
463
464         channel = g_io_channel_unix_new(fd);
465         if (channel == NULL)
466                 return;
467
468         udev_watch = g_io_add_watch(channel,
469                                 G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL,
470                                                         udev_event, NULL);
471
472         g_io_channel_unref(channel);
473 }
474
475 static int udev_init(void)
476 {
477         devpath_list = g_hash_table_new_full(g_str_hash, g_str_equal,
478                                                 g_free, g_free);
479         if (devpath_list == NULL) {
480                 ofono_error("Failed to create udev path list");
481                 return -ENOMEM;
482         }
483
484         udev_ctx = udev_new();
485         if (udev_ctx == NULL) {
486                 ofono_error("Failed to create udev context");
487                 g_hash_table_destroy(devpath_list);
488                 return -EIO;
489         }
490
491         udev_mon = udev_monitor_new_from_netlink(udev_ctx, "udev");
492         if (udev_mon == NULL) {
493                 ofono_error("Failed to create udev monitor");
494                 g_hash_table_destroy(devpath_list);
495                 udev_unref(udev_ctx);
496                 udev_ctx = NULL;
497                 return -EIO;
498         }
499
500         udev_monitor_filter_add_match_subsystem_devtype(udev_mon, "tty", NULL);
501         udev_monitor_filter_add_match_subsystem_devtype(udev_mon, "net", NULL);
502         udev_monitor_filter_add_match_subsystem_devtype(udev_mon, "hsi", NULL);
503
504         udev_monitor_filter_update(udev_mon);
505
506         udev_start();
507
508         return 0;
509 }
510
511 static void udev_exit(void)
512 {
513         GSList *list;
514
515         if (udev_watch > 0)
516                 g_source_remove(udev_watch);
517
518         for (list = modem_list; list; list = list->next) {
519                 struct ofono_modem *modem = list->data;
520
521                 ofono_modem_remove(modem);
522         }
523
524         g_slist_free(modem_list);
525         modem_list = NULL;
526
527         g_hash_table_destroy(devpath_list);
528         devpath_list = NULL;
529
530         if (udev_ctx == NULL)
531                 return;
532
533         udev_monitor_filter_remove(udev_mon);
534
535         udev_monitor_unref(udev_mon);
536         udev_unref(udev_ctx);
537 }
538
539 OFONO_PLUGIN_DEFINE(udev, "udev hardware detection", VERSION,
540                         OFONO_PLUGIN_PRIORITY_DEFAULT, udev_init, udev_exit)