872039a39425274024832ecf1dde05c40b3da697
[profile/ivi/ofono.git] / plugins / udevng.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 #include <string.h>
30
31 #include <libudev.h>
32
33 #include <glib.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 struct modem_info {
41         char *syspath;
42         char *devname;
43         char *driver;
44         char *vendor;
45         char *model;
46         GSList *devices;
47         struct ofono_modem *modem;
48         const char *sysattr;
49 };
50
51 struct device_info {
52         char *devpath;
53         char *devnode;
54         char *interface;
55         char *number;
56         char *label;
57         char *sysattr;
58 };
59
60 static gboolean setup_isi(struct modem_info *modem)
61 {
62         const char *node = NULL;
63         int addr = 0;
64         GSList *list;
65
66         DBG("%s", modem->syspath);
67
68         for (list = modem->devices; list; list = list->next) {
69                 struct device_info *info = list->data;
70
71                 DBG("%s %s %s %s %s", info->devnode, info->interface,
72                                 info->number, info->label, info->sysattr);
73
74                 if (g_strcmp0(info->sysattr, "820") == 0) {
75                         if (g_strcmp0(info->interface, "2/254/0") == 0)
76                                 addr = 16;
77
78                         node = info->devnode;
79                 }
80         }
81
82         if (node == NULL)
83                 return FALSE;
84
85         DBG("interface=%s address=%d", node, addr);
86
87         ofono_modem_set_string(modem->modem, "Interface", node);
88         ofono_modem_set_integer(modem->modem, "Address", addr);
89
90         return TRUE;
91 }
92
93 static gboolean setup_mbm(struct modem_info *modem)
94 {
95         const char *mdm = NULL, *app = NULL, *network = NULL, *gps = NULL;
96         GSList *list;
97
98         DBG("%s", modem->syspath);
99
100         for (list = modem->devices; list; list = list->next) {
101                 struct device_info *info = list->data;
102
103                 DBG("%s %s %s %s %s", info->devnode, info->interface,
104                                 info->number, info->label, info->sysattr);
105
106                 if (g_str_has_suffix(info->sysattr, "Modem") == TRUE ||
107                                 g_str_has_suffix(info->sysattr,
108                                                         "Modem 2") == TRUE) {
109                         if (mdm == NULL)
110                                 mdm = info->devnode;
111                         else
112                                 app = info->devnode;
113                 } else if (g_str_has_suffix(info->sysattr,
114                                                 "GPS Port") == TRUE ||
115                                 g_str_has_suffix(info->sysattr,
116                                                 "Module NMEA") == TRUE) {
117                         gps = info->devnode;
118                 } else if (g_str_has_suffix(info->sysattr,
119                                                 "Network Adapter") == TRUE ||
120                                 g_str_has_suffix(info->sysattr,
121                                                 "NetworkAdapter") == TRUE) {
122                         network = info->devnode;
123                 }
124         }
125
126         if (mdm == NULL || app == NULL)
127                 return FALSE;
128
129         DBG("modem=%s data=%s network=%s gps=%s", mdm, app, network, gps);
130
131         ofono_modem_set_string(modem->modem, "ModemDevice", mdm);
132         ofono_modem_set_string(modem->modem, "DataDevice", app);
133         ofono_modem_set_string(modem->modem, "GPSDevice", gps);
134         ofono_modem_set_string(modem->modem, "NetworkInterface", network);
135
136         return TRUE;
137 }
138
139 static gboolean setup_hso(struct modem_info *modem)
140 {
141         const char *ctl = NULL, *app = NULL, *mdm = NULL, *net = NULL;
142         GSList *list;
143
144         DBG("%s", modem->syspath);
145
146         for (list = modem->devices; list; list = list->next) {
147                 struct device_info *info = list->data;
148
149                 DBG("%s %s %s %s %s", info->devnode, info->interface,
150                                 info->number, info->label, info->sysattr);
151
152                 if (g_strcmp0(info->sysattr, "Control") == 0)
153                         ctl = info->devnode;
154                 else if (g_strcmp0(info->sysattr, "Application") == 0)
155                         app = info->devnode;
156                 else if (g_strcmp0(info->sysattr, "Modem") == 0)
157                         mdm = info->devnode;
158                 else if (info->sysattr == NULL &&
159                                 g_str_has_prefix(info->devnode, "hso") == TRUE)
160                         net = info->devnode;
161         }
162
163         if (ctl == NULL || app == NULL)
164                 return FALSE;
165
166         DBG("control=%s application=%s modem=%s network=%s",
167                                                 ctl, app, mdm, net);
168
169         ofono_modem_set_string(modem->modem, "Control", ctl);
170         ofono_modem_set_string(modem->modem, "Application", app);
171         ofono_modem_set_string(modem->modem, "Modem", mdm);
172         ofono_modem_set_string(modem->modem, "NetworkInterface", net);
173
174         return TRUE;
175 }
176
177 static gboolean setup_gobi(struct modem_info *modem)
178 {
179         const char *qmi = NULL, *mdm = NULL, *net = NULL;
180         const char *gps = NULL, *diag = NULL;
181         GSList *list;
182
183         DBG("%s", modem->syspath);
184
185         for (list = modem->devices; list; list = list->next) {
186                 struct device_info *info = list->data;
187
188                 DBG("%s %s %s %s", info->devnode, info->interface,
189                                                 info->number, info->label);
190
191                 if (g_strcmp0(info->interface, "255/255/255") == 0) {
192                         if (info->number == NULL)
193                                 qmi = info->devnode;
194                         else if (g_strcmp0(info->number, "00") == 0)
195                                 net = info->devnode;
196                         else if (g_strcmp0(info->number, "01") == 0)
197                                 diag = info->devnode;
198                         else if (g_strcmp0(info->number, "02") == 0)
199                                 mdm = info->devnode;
200                         else if (g_strcmp0(info->number, "03") == 0)
201                                 gps = info->devnode;
202                 }
203         }
204
205         if (qmi == NULL || mdm == NULL || net == NULL)
206                 return FALSE;
207
208         DBG("qmi=%s net=%s mdm=%s gps=%s diag=%s", qmi, net, mdm, gps, diag);
209
210         ofono_modem_set_string(modem->modem, "Device", qmi);
211         ofono_modem_set_string(modem->modem, "Modem", mdm);
212         ofono_modem_set_string(modem->modem, "Diag", diag);
213         ofono_modem_set_string(modem->modem, "NetworkInterface", net);
214
215         return TRUE;
216 }
217
218 static gboolean setup_sierra(struct modem_info *modem)
219 {
220         const char *mdm = NULL, *app = NULL, *net = NULL, *diag = NULL;
221         GSList *list;
222
223         DBG("%s", modem->syspath);
224
225         for (list = modem->devices; list; list = list->next) {
226                 struct device_info *info = list->data;
227
228                 DBG("%s %s %s %s", info->devnode, info->interface,
229                                                 info->number, info->label);
230
231                 if (g_strcmp0(info->interface, "255/255/255") == 0) {
232                         if (g_strcmp0(info->number, "01") == 0)
233                                 diag = info->devnode;
234                         if (g_strcmp0(info->number, "03") == 0)
235                                 mdm = info->devnode;
236                         else if (g_strcmp0(info->number, "04") == 0)
237                                 app = info->devnode;
238                         else if (g_strcmp0(info->number, "07") == 0)
239                                 net = info->devnode;
240                 }
241         }
242
243         if (mdm == NULL || net == NULL)
244                 return FALSE;
245
246         DBG("modem=%s app=%s net=%s diag=%s", mdm, app, net, diag);
247
248         ofono_modem_set_string(modem->modem, "Modem", mdm);
249         ofono_modem_set_string(modem->modem, "App", app);
250         ofono_modem_set_string(modem->modem, "Diag", diag);
251         ofono_modem_set_string(modem->modem, "NetworkInterface", net);
252
253         return TRUE;
254 }
255
256 static gboolean setup_option(struct modem_info *modem)
257 {
258         const char *aux = NULL, *mdm = NULL, *diag = NULL;
259         GSList *list;
260
261         DBG("%s", modem->syspath);
262
263         for (list = modem->devices; list; list = list->next) {
264                 struct device_info *info = list->data;
265
266                 DBG("%s %s %s %s", info->devnode, info->interface,
267                                                 info->number, info->label);
268
269                 if (g_strcmp0(info->interface, "255/255/255") == 0) {
270                         if (g_strcmp0(info->number, "00") == 0)
271                                 mdm = info->devnode;
272                         else if (g_strcmp0(info->number, "01") == 0)
273                                 diag = info->devnode;
274                         else if (g_strcmp0(info->number, "02") == 0)
275                                 aux = info->devnode;
276                 }
277
278         }
279
280         if (aux == NULL || mdm == NULL)
281                 return FALSE;
282
283         DBG("aux=%s modem=%s diag=%s", aux, mdm, diag);
284
285         ofono_modem_set_string(modem->modem, "Aux", aux);
286         ofono_modem_set_string(modem->modem, "Modem", mdm);
287         ofono_modem_set_string(modem->modem, "Diag", diag);
288
289         return TRUE;
290 }
291
292 static gboolean setup_huawei(struct modem_info *modem)
293 {
294         const char *qmi = NULL, *mdm = NULL, *net = NULL;
295         const char *pcui = NULL, *diag = NULL;
296         GSList *list;
297
298         DBG("%s", modem->syspath);
299
300         for (list = modem->devices; list; list = list->next) {
301                 struct device_info *info = list->data;
302
303                 DBG("%s %s %s %s", info->devnode, info->interface,
304                                                 info->number, info->label);
305
306                 if (g_strcmp0(info->label, "modem") == 0 ||
307                                 g_strcmp0(info->interface, "255/1/1") == 0 ||
308                                 g_strcmp0(info->interface, "255/2/1") == 0) {
309                         mdm = info->devnode;
310                 } else if (g_strcmp0(info->label, "pcui") == 0 ||
311                                 g_strcmp0(info->interface, "255/1/2") == 0 ||
312                                 g_strcmp0(info->interface, "255/2/2") == 0) {
313                         pcui = info->devnode;
314                 } else if (g_strcmp0(info->label, "diag") == 0 ||
315                                 g_strcmp0(info->interface, "255/1/3") == 0 ||
316                                 g_strcmp0(info->interface, "255/2/3") == 0) {
317                         diag = info->devnode;
318                 } else if (g_strcmp0(info->interface, "255/1/8") == 0) {
319                         net = info->devnode;
320                 } else if (g_strcmp0(info->interface, "255/255/255") == 0) {
321                         if (g_strcmp0(info->number, "00") == 0)
322                                 mdm = info->devnode;
323                         else if (g_strcmp0(info->number, "01") == 0)
324                                 pcui = info->devnode;
325                         else if (g_strcmp0(info->number, "02") == 0)
326                                 pcui = info->devnode;
327                         else if (g_strcmp0(info->number, "03") == 0)
328                                 pcui = info->devnode;
329                         else if (g_strcmp0(info->number, "04") == 0)
330                                 pcui = info->devnode;
331                 }
332         }
333
334         if (mdm == NULL || pcui == NULL)
335                 return FALSE;
336
337         DBG("mdm=%s pcui=%s diag=%s qmi=%s net=%s", mdm, pcui, diag, qmi, net);
338
339         ofono_modem_set_string(modem->modem, "Device", qmi);
340         ofono_modem_set_string(modem->modem, "Modem", mdm);
341         ofono_modem_set_string(modem->modem, "Pcui", pcui);
342         ofono_modem_set_string(modem->modem, "Diag", diag);
343         ofono_modem_set_string(modem->modem, "NetworkInterface", net);
344
345         return TRUE;
346 }
347
348 static gboolean setup_speedup(struct modem_info *modem)
349 {
350         const char *aux = NULL, *mdm = NULL;
351         GSList *list;
352
353         DBG("%s", modem->syspath);
354
355         for (list = modem->devices; list; list = list->next) {
356                 struct device_info *info = list->data;
357
358                 DBG("%s %s %s %s", info->devnode, info->interface,
359                                                 info->number, info->label);
360
361                 if (g_strcmp0(info->label, "aux") == 0) {
362                         aux = info->devnode;
363                         if (mdm != NULL)
364                                 break;
365                 } else if (g_strcmp0(info->label, "modem") == 0) {
366                         mdm = info->devnode;
367                         if (aux != NULL)
368                                 break;
369                 }
370         }
371
372         if (aux == NULL || mdm == NULL)
373                 return FALSE;
374
375         DBG("aux=%s modem=%s", aux, mdm);
376
377         ofono_modem_set_string(modem->modem, "Aux", aux);
378         ofono_modem_set_string(modem->modem, "Modem", mdm);
379
380         return TRUE;
381 }
382
383 static gboolean setup_linktop(struct modem_info *modem)
384 {
385         const char *aux = NULL, *mdm = NULL;
386         GSList *list;
387
388         DBG("%s", modem->syspath);
389
390         for (list = modem->devices; list; list = list->next) {
391                 struct device_info *info = list->data;
392
393                 DBG("%s %s %s %s", info->devnode, info->interface,
394                                                 info->number, info->label);
395
396                 if (g_strcmp0(info->interface, "2/2/1") == 0) {
397                         if (g_strcmp0(info->number, "01") == 0)
398                                 aux = info->devnode;
399                         else if (g_strcmp0(info->number, "03") == 0)
400                                 mdm = info->devnode;
401                 }
402         }
403
404         if (aux == NULL || mdm == NULL)
405                 return FALSE;
406
407         DBG("aux=%s modem=%s", aux, mdm);
408
409         ofono_modem_set_string(modem->modem, "Aux", aux);
410         ofono_modem_set_string(modem->modem, "Modem", mdm);
411
412         return TRUE;
413 }
414
415 static gboolean setup_icera(struct modem_info *modem)
416 {
417         const char *aux = NULL, *mdm = NULL, *net = NULL;
418         GSList *list;
419
420         DBG("%s", modem->syspath);
421
422         for (list = modem->devices; list; list = list->next) {
423                 struct device_info *info = list->data;
424
425                 DBG("%s %s %s %s", info->devnode, info->interface,
426                                                 info->number, info->label);
427
428                 if (g_strcmp0(info->interface, "2/2/1") == 0) {
429                         if (g_strcmp0(info->number, "00") == 0)
430                                 aux = info->devnode;
431                         else if (g_strcmp0(info->number, "01") == 0)
432                                 aux = info->devnode;
433                         else if (g_strcmp0(info->number, "02") == 0)
434                                 mdm = info->devnode;
435                         else if (g_strcmp0(info->number, "03") == 0)
436                                 mdm = info->devnode;
437                 } else if (g_strcmp0(info->interface, "2/6/0") == 0) {
438                         if (g_strcmp0(info->number, "05") == 0)
439                                 net = info->devnode;
440                         else if (g_strcmp0(info->number, "06") == 0)
441                                 net = info->devnode;
442                         else if (g_strcmp0(info->number, "07") == 0)
443                                 net = info->devnode;
444                 }
445         }
446
447         if (aux == NULL || mdm == NULL)
448                 return FALSE;
449
450         DBG("aux=%s modem=%s net=%s", aux, mdm, net);
451
452         ofono_modem_set_string(modem->modem, "Aux", aux);
453         ofono_modem_set_string(modem->modem, "Modem", mdm);
454         ofono_modem_set_string(modem->modem, "NetworkInterface", net);
455
456         return TRUE;
457 }
458
459 static gboolean setup_alcatel(struct modem_info *modem)
460 {
461         const char *aux = NULL, *mdm = NULL;
462         GSList *list;
463
464         DBG("%s", modem->syspath);
465
466         for (list = modem->devices; list; list = list->next) {
467                 struct device_info *info = list->data;
468
469                 DBG("%s %s %s %s", info->devnode, info->interface,
470                                                 info->number, info->label);
471
472                 if (g_strcmp0(info->label, "aux") == 0) {
473                         aux = info->devnode;
474                         if (mdm != NULL)
475                                 break;
476                 } else if (g_strcmp0(info->label, "modem") == 0) {
477                         mdm = info->devnode;
478                         if (aux != NULL)
479                                 break;
480                 } else if (g_strcmp0(info->interface, "255/255/255") == 0) {
481                         if (g_strcmp0(info->number, "03") == 0)
482                                 aux = info->devnode;
483                         else if (g_strcmp0(info->number, "05") == 0)
484                                 mdm = info->devnode;
485                 }
486         }
487
488         if (aux == NULL || mdm == NULL)
489                 return FALSE;
490
491         DBG("aux=%s modem=%s", aux, mdm);
492
493         ofono_modem_set_string(modem->modem, "Aux", aux);
494         ofono_modem_set_string(modem->modem, "Modem", mdm);
495
496         return TRUE;
497 }
498
499 static gboolean setup_novatel(struct modem_info *modem)
500 {
501         const char *aux = NULL, *mdm = NULL;
502         GSList *list;
503
504         DBG("%s", modem->syspath);
505
506         for (list = modem->devices; list; list = list->next) {
507                 struct device_info *info = list->data;
508
509                 DBG("%s %s %s %s", info->devnode, info->interface,
510                                                 info->number, info->label);
511
512                 if (g_strcmp0(info->label, "aux") == 0) {
513                         aux = info->devnode;
514                         if (mdm != NULL)
515                                 break;
516                 } else if (g_strcmp0(info->label, "modem") == 0) {
517                         mdm = info->devnode;
518                         if (aux != NULL)
519                                 break;
520                 } else if (g_strcmp0(info->interface, "255/255/255") == 0) {
521                         if (g_strcmp0(info->number, "00") == 0)
522                                 aux = info->devnode;
523                         else if (g_strcmp0(info->number, "01") == 0)
524                                 mdm = info->devnode;
525                 }
526         }
527
528         if (aux == NULL || mdm == NULL)
529                 return FALSE;
530
531         DBG("aux=%s modem=%s", aux, mdm);
532
533         ofono_modem_set_string(modem->modem, "Aux", aux);
534         ofono_modem_set_string(modem->modem, "Modem", mdm);
535
536         return TRUE;
537 }
538
539 static gboolean setup_nokia(struct modem_info *modem)
540 {
541         const char *aux = NULL, *mdm = NULL;
542         GSList *list;
543
544         DBG("%s", modem->syspath);
545
546         for (list = modem->devices; list; list = list->next) {
547                 struct device_info *info = list->data;
548
549                 DBG("%s %s %s %s", info->devnode, info->interface,
550                                                 info->number, info->label);
551
552                 if (g_strcmp0(info->label, "aux") == 0) {
553                         aux = info->devnode;
554                         if (mdm != NULL)
555                                 break;
556                 } else if (g_strcmp0(info->label, "modem") == 0) {
557                         mdm = info->devnode;
558                         if (aux != NULL)
559                                 break;
560                 } else if (g_strcmp0(info->interface, "10/0/0") == 0) {
561                         if (g_strcmp0(info->number, "02") == 0)
562                                 mdm = info->devnode;
563                         else if (g_strcmp0(info->number, "04") == 0)
564                                 aux = info->devnode;
565                 }
566         }
567
568         if (aux == NULL || mdm == NULL)
569                 return FALSE;
570
571         DBG("aux=%s modem=%s", aux, mdm);
572
573         ofono_modem_set_string(modem->modem, "Aux", aux);
574         ofono_modem_set_string(modem->modem, "Modem", mdm);
575
576         return TRUE;
577 }
578
579 static gboolean setup_telit(struct modem_info *modem)
580 {
581         const char *mdm = NULL, *aux = NULL, *gps = NULL, *diag = NULL;
582         GSList *list;
583
584         DBG("%s", modem->syspath);
585
586         for (list = modem->devices; list; list = list->next) {
587                 struct device_info *info = list->data;
588
589                 DBG("%s %s %s %s", info->devnode, info->interface,
590                                                 info->number, info->label);
591
592                 if (g_strcmp0(info->label, "aux") == 0) {
593                         aux = info->devnode;
594                         if (mdm != NULL)
595                                 break;
596                 } else if (g_strcmp0(info->label, "modem") == 0) {
597                         mdm = info->devnode;
598                         if (aux != NULL)
599                                 break;
600                 } else if (g_strcmp0(info->interface, "255/255/255") == 0) {
601                         if (g_strcmp0(info->number, "00") == 0)
602                                 mdm = info->devnode;
603                         else if (g_strcmp0(info->number, "01") == 0)
604                                 diag = info->devnode;
605                         else if (g_strcmp0(info->number, "02") == 0)
606                                 gps = info->devnode;
607                         else if (g_strcmp0(info->number, "03") == 0)
608                                 aux = info->devnode;
609                 }
610         }
611
612         if (aux == NULL || mdm == NULL)
613                 return FALSE;
614
615         DBG("modem=%s aux=%s gps=%s diag=%s", mdm, aux, gps, diag);
616
617         ofono_modem_set_string(modem->modem, "Modem", mdm);
618         ofono_modem_set_string(modem->modem, "Data", aux);
619         ofono_modem_set_string(modem->modem, "GPS", gps);
620
621         return TRUE;
622 }
623
624 static gboolean setup_simcom(struct modem_info *modem)
625 {
626         const char *mdm = NULL, *aux = NULL, *gps = NULL, *diag = NULL;
627         GSList *list;
628
629         DBG("%s", modem->syspath);
630
631         for (list = modem->devices; list; list = list->next) {
632                 struct device_info *info = list->data;
633
634                 DBG("%s %s %s %s", info->devnode, info->interface,
635                                                 info->number, info->label);
636
637                 if (g_strcmp0(info->label, "aux") == 0) {
638                         aux = info->devnode;
639                         if (mdm != NULL)
640                                 break;
641                 } else if (g_strcmp0(info->label, "modem") == 0) {
642                         mdm = info->devnode;
643                         if (aux != NULL)
644                                 break;
645                 } else if (g_strcmp0(info->interface, "255/255/255") == 0) {
646                         if (g_strcmp0(info->number, "00") == 0)
647                                 diag = info->devnode;
648                         else if (g_strcmp0(info->number, "01") == 0)
649                                 gps = info->devnode;
650                         else if (g_strcmp0(info->number, "02") == 0)
651                                 aux = info->devnode;
652                         else if (g_strcmp0(info->number, "03") == 0)
653                                 mdm = info->devnode;
654                 }
655         }
656
657         if (aux == NULL || mdm == NULL)
658                 return FALSE;
659
660         DBG("modem=%s aux=%s gps=%s diag=%s", mdm, aux, gps, diag);
661
662         ofono_modem_set_string(modem->modem, "Modem", mdm);
663         ofono_modem_set_string(modem->modem, "Data", aux);
664         ofono_modem_set_string(modem->modem, "GPS", gps);
665
666         return TRUE;
667 }
668
669 static gboolean setup_zte(struct modem_info *modem)
670 {
671         const char *aux = NULL, *mdm = NULL, *qcdm = NULL;
672         const char *modem_intf;
673         GSList *list;
674
675         DBG("%s", modem->syspath);
676
677         if (g_strcmp0(modem->model, "0016") == 0 ||
678                                 g_strcmp0(modem->model, "0017") == 0 ||
679                                 g_strcmp0(modem->model, "0117") == 0)
680                 modem_intf = "02";
681         else
682                 modem_intf = "03";
683
684         for (list = modem->devices; list; list = list->next) {
685                 struct device_info *info = list->data;
686
687                 DBG("%s %s %s %s", info->devnode, info->interface,
688                                                 info->number, info->label);
689
690                 if (g_strcmp0(info->label, "aux") == 0) {
691                         aux = info->devnode;
692                         if (mdm != NULL)
693                                 break;
694                 } else if (g_strcmp0(info->label, "modem") == 0) {
695                         mdm = info->devnode;
696                         if (aux != NULL)
697                                 break;
698                 } else if (g_strcmp0(info->interface, "255/255/255") == 0) {
699                         if (g_strcmp0(info->number, "00") == 0)
700                                 qcdm = info->devnode;
701                         else if (g_strcmp0(info->number, "01") == 0)
702                                 aux = info->devnode;
703                         else if (g_strcmp0(info->number, modem_intf) == 0)
704                                 mdm = info->devnode;
705                 }
706         }
707
708         if (aux == NULL || mdm == NULL)
709                 return FALSE;
710
711         DBG("aux=%s modem=%s qcdm=%s", aux, mdm, qcdm);
712
713         ofono_modem_set_string(modem->modem, "Aux", aux);
714         ofono_modem_set_string(modem->modem, "Modem", mdm);
715
716         return TRUE;
717 }
718
719 static gboolean setup_samsung(struct modem_info *modem)
720 {
721         const char *control = NULL, *network = NULL;
722         GSList *list;
723
724         DBG("%s", modem->syspath);
725
726         for (list = modem->devices; list; list = list->next) {
727                 struct device_info *info = list->data;
728
729                 DBG("%s %s %s %s", info->devnode, info->interface,
730                                                 info->number, info->label);
731
732                 if (g_strcmp0(info->interface, "10/0/0") == 0)
733                         control = info->devnode;
734                 else if (g_strcmp0(info->interface, "255/0/0") == 0)
735                         network = info->devnode;
736         }
737
738         if (control == NULL && network == NULL)
739                 return FALSE;
740
741         DBG("control=%s network=%s", control, network);
742
743         ofono_modem_set_string(modem->modem, "ControlPort", control);
744         ofono_modem_set_string(modem->modem, "NetworkInterface", network);
745
746         return TRUE;
747 }
748
749 static struct {
750         const char *name;
751         gboolean (*setup)(struct modem_info *modem);
752         const char *sysattr;
753 } driver_list[] = {
754         { "isiusb",     setup_isi,      "type"                  },
755         { "mbm",        setup_mbm,      "device/interface"      },
756         { "hso",        setup_hso,      "hsotype"               },
757         { "gobi",       setup_gobi      },
758         { "sierra",     setup_sierra    },
759         { "option",     setup_option    },
760         { "huawei",     setup_huawei    },
761         { "speedupcdma",setup_speedup   },
762         { "speedup",    setup_speedup   },
763         { "linktop",    setup_linktop   },
764         { "alcatel",    setup_alcatel   },
765         { "novatel",    setup_novatel   },
766         { "nokia",      setup_nokia     },
767         { "telit",      setup_telit     },
768         { "simcom",     setup_simcom    },
769         { "zte",        setup_zte       },
770         { "icera",      setup_icera     },
771         { "samsung",    setup_samsung   },
772         { }
773 };
774
775 static GHashTable *modem_list;
776
777 static const char *get_sysattr(const char *driver)
778 {
779         unsigned int i;
780
781         for (i = 0; driver_list[i].name; i++) {
782                 if (g_str_equal(driver_list[i].name, driver) == TRUE)
783                         return driver_list[i].sysattr;
784         }
785
786         return NULL;
787 }
788
789 static void destroy_modem(gpointer data)
790 {
791         struct modem_info *modem = data;
792         GSList *list;
793
794         DBG("%s", modem->syspath);
795
796         ofono_modem_remove(modem->modem);
797
798         for (list = modem->devices; list; list = list->next) {
799                 struct device_info *info = list->data;
800
801                 DBG("%s", info->devnode);
802
803                 g_free(info->devpath);
804                 g_free(info->devnode);
805                 g_free(info->interface);
806                 g_free(info->number);
807                 g_free(info->label);
808                 g_free(info->sysattr);
809                 g_free(info);
810
811                 list->data = NULL;
812         }
813
814         g_slist_free(modem->devices);
815
816         g_free(modem->syspath);
817         g_free(modem->devname);
818         g_free(modem->driver);
819         g_free(modem->vendor);
820         g_free(modem->model);
821         g_free(modem);
822 }
823
824 static gboolean check_remove(gpointer key, gpointer value, gpointer user_data)
825 {
826         struct modem_info *modem = value;
827         const char *devpath = user_data;
828         GSList *list;
829
830         for (list = modem->devices; list; list = list->next) {
831                 struct device_info *info = list->data;
832
833                 if (g_strcmp0(info->devpath, devpath) == 0)
834                         return TRUE;
835         }
836
837         return FALSE;
838 }
839
840 static void remove_device(struct udev_device *device)
841 {
842         const char *syspath;
843
844         syspath = udev_device_get_syspath(device);
845         if (syspath == NULL)
846                 return;
847
848         DBG("%s", syspath);
849
850         g_hash_table_foreach_remove(modem_list, check_remove,
851                                                 (char *) syspath);
852 }
853
854 static gint compare_device(gconstpointer a, gconstpointer b)
855 {
856         const struct device_info *info1 = a;
857         const struct device_info *info2 = b;
858
859         return g_strcmp0(info1->number, info2->number);
860 }
861
862 static void add_device(const char *syspath, const char *devname,
863                         const char *driver, const char *vendor,
864                         const char *model, struct udev_device *device)
865 {
866         struct udev_device *intf;
867         const char *devpath, *devnode, *interface, *number, *label, *sysattr;
868         struct modem_info *modem;
869         struct device_info *info;
870
871         devpath = udev_device_get_syspath(device);
872         if (devpath == NULL)
873                 return;
874
875         devnode = udev_device_get_devnode(device);
876         if (devnode == NULL) {
877                 devnode = udev_device_get_property_value(device, "INTERFACE");
878                 if (devnode == NULL)
879                         return;
880         }
881
882         intf = udev_device_get_parent_with_subsystem_devtype(device,
883                                                 "usb", "usb_interface");
884         if (intf == NULL)
885                 return;
886
887         modem = g_hash_table_lookup(modem_list, syspath);
888         if (modem == NULL) {
889                 modem = g_try_new0(struct modem_info, 1);
890                 if (modem == NULL)
891                         return;
892
893                 modem->syspath = g_strdup(syspath);
894                 modem->devname = g_strdup(devname);
895                 modem->driver = g_strdup(driver);
896                 modem->vendor = g_strdup(vendor);
897                 modem->model = g_strdup(model);
898
899                 modem->sysattr = get_sysattr(driver);
900
901                 g_hash_table_replace(modem_list, modem->syspath, modem);
902         }
903
904         interface = udev_device_get_property_value(intf, "INTERFACE");
905         number = udev_device_get_property_value(device, "ID_USB_INTERFACE_NUM");
906
907         label = udev_device_get_property_value(device, "OFONO_LABEL");
908
909         if (modem->sysattr != NULL)
910                 sysattr = udev_device_get_sysattr_value(device, modem->sysattr);
911         else
912                 sysattr = NULL;
913
914         DBG("%s", syspath);
915         DBG("%s", devpath);
916         DBG("%s (%s) %s [%s] ==> %s %s", devnode, driver,
917                                         interface, number, label, sysattr);
918
919         info = g_try_new0(struct device_info, 1);
920         if (info == NULL)
921                 return;
922
923         info->devpath = g_strdup(devpath);
924         info->devnode = g_strdup(devnode);
925         info->interface = g_strdup(interface);
926         info->number = g_strdup(number);
927         info->label = g_strdup(label);
928         info->sysattr = g_strdup(sysattr);
929
930         modem->devices = g_slist_insert_sorted(modem->devices, info,
931                                                         compare_device);
932 }
933
934 static struct {
935         const char *driver;
936         const char *drv;
937         const char *vid;
938         const char *pid;
939 } vendor_list[] = {
940         { "isiusb",     "cdc_phonet"                    },
941         { "linktop",    "cdc_acm",      "230d"          },
942         { "icera",      "cdc_acm",      "19d2"          },
943         { "icera",      "cdc_ether",    "19d2"          },
944         { "icera",      "cdc_acm",      "04e8", "6872"  },
945         { "icera",      "cdc_ether",    "04e8", "6872"  },
946         { "icera",      "cdc_acm",      "0421", "0633"  },
947         { "icera",      "cdc_ether",    "0421", "0633"  },
948         { "mbm",        "cdc_acm",      "0bdb"          },
949         { "mbm"         "cdc_ether",    "0bdb"          },
950         { "mbm",        "cdc_acm",      "0fce"          },
951         { "mbm",        "cdc_ether",    "0fce"          },
952         { "mbm",        "cdc_acm",      "413c"          },
953         { "mbm",        "cdc_ether",    "413c"          },
954         { "mbm",        "cdc_acm",      "03f0"          },
955         { "mbm",        "cdc_ether",    "03f0"          },
956         { "mbm",        "cdc_acm",      "0930"          },
957         { "mbm",        "cdc_ether",    "0930"          },
958         { "hso",        "hso"                           },
959         { "gobi",       "qmi_wwan"                      },
960         { "gobi",       "qcserial"                      },
961         { "sierra",     "sierra"                        },
962         { "sierra",     "sierra_net"                    },
963         { "option",     "option",       "0af0"          },
964         { "huawei",     "option",       "201e"          },
965         { "huawei",     "cdc_ether",    "12d1"          },
966         { "huawei",     "qmi_wwan",     "12d1"          },
967         { "huawei",     "option",       "12d1"          },
968         { "speedupcdma","option",       "1c9e", "9e00"  },
969         { "speedup",    "option",       "1c9e"          },
970         { "speedup",    "option",       "2020"          },
971         { "alcatel",    "option",       "1bbb", "0017"  },
972         { "novatel",    "option",       "1410"          },
973         { "zte",        "option",       "19d2"          },
974         { "simcom",     "option",       "05c6", "9000"  },
975         { "telit",      "usbserial",    "1bc7"          },
976         { "telit",      "option",       "1bc7"          },
977         { "nokia",      "option",       "0421", "060e"  },
978         { "nokia",      "option",       "0421", "0623"  },
979         { "samsung",    "option",       "04e8", "6889"  },
980         { "samsung",    "kalmia"                        },
981         { }
982 };
983
984 static void check_usb_device(struct udev_device *device)
985 {
986         struct udev_device *usb_device;
987         const char *syspath, *devname, *driver;
988         const char *vendor = NULL, *model = NULL;
989
990         usb_device = udev_device_get_parent_with_subsystem_devtype(device,
991                                                         "usb", "usb_device");
992         if (usb_device == NULL)
993                 return;
994
995         syspath = udev_device_get_syspath(usb_device);
996         if (syspath == NULL)
997                 return;
998
999         devname = udev_device_get_devnode(usb_device);
1000         if (devname == NULL)
1001                 return;
1002
1003         driver = udev_device_get_property_value(usb_device, "OFONO_DRIVER");
1004         if (driver == NULL) {
1005                 const char *drv, *vid, *pid;
1006                 unsigned int i;
1007
1008                 drv = udev_device_get_property_value(device, "ID_USB_DRIVER");
1009                 if (drv == NULL) {
1010                         drv = udev_device_get_driver(device);
1011                         if (drv == NULL) {
1012                                 struct udev_device *parent;
1013
1014                                 parent = udev_device_get_parent(device);
1015                                 if (parent == NULL)
1016                                         return;
1017
1018                                 drv = udev_device_get_driver(parent);
1019                                 if (drv == NULL)
1020                                         return;
1021                         }
1022                 }
1023
1024                 vid = udev_device_get_property_value(device, "ID_VENDOR_ID");
1025                 pid = udev_device_get_property_value(device, "ID_MODEL_ID");
1026
1027                 DBG("%s [%s:%s]", drv, vid, pid);
1028
1029                 for (i = 0; vendor_list[i].driver; i++) {
1030                         if (g_str_equal(vendor_list[i].drv, drv) == FALSE)
1031                                 continue;
1032
1033                         if (vendor_list[i].vid == NULL) {
1034                                 driver = vendor_list[i].driver;
1035                                 vendor = vid;
1036                                 model = pid;
1037                                 continue;
1038                         }
1039
1040                         if (vid == NULL || pid == NULL)
1041                                 continue;
1042
1043                         if (g_str_equal(vendor_list[i].vid, vid) == TRUE) {
1044                                 if (vendor_list[i].pid == NULL) {
1045                                         driver = vendor_list[i].driver;
1046                                         vendor = vid;
1047                                         model = pid;
1048                                         continue;
1049                                 }
1050
1051                                 if (g_strcmp0(vendor_list[i].pid, pid) == 0) {
1052                                         driver = vendor_list[i].driver;
1053                                         vendor = vid;
1054                                         model = pid;
1055                                         break;
1056                                 }
1057                         }
1058                 }
1059
1060                 if (driver == NULL)
1061                         return;
1062         }
1063
1064         add_device(syspath, devname, driver, vendor, model, device);
1065 }
1066
1067 static void check_device(struct udev_device *device)
1068 {
1069         const char *bus;
1070
1071         bus = udev_device_get_property_value(device, "ID_BUS");
1072         if (bus == NULL) {
1073                 bus = udev_device_get_subsystem(device);
1074                 if (bus == NULL)
1075                         return;
1076         }
1077
1078         if (g_str_equal(bus, "usb") == TRUE)
1079                 check_usb_device(device);
1080 }
1081
1082 static gboolean create_modem(gpointer key, gpointer value, gpointer user_data)
1083 {
1084         struct modem_info *modem = value;
1085         const char *syspath = key;
1086         unsigned int i;
1087
1088         if (modem->modem != NULL)
1089                 return FALSE;
1090
1091         DBG("%s", syspath);
1092
1093         if (modem->devices == NULL)
1094                 return TRUE;
1095
1096         DBG("driver=%s", modem->driver);
1097
1098         modem->modem = ofono_modem_create(NULL, modem->driver);
1099         if (modem->modem == NULL)
1100                 return TRUE;
1101
1102         for (i = 0; driver_list[i].name; i++) {
1103                 if (g_str_equal(driver_list[i].name, modem->driver) == FALSE)
1104                         continue;
1105
1106                 if (driver_list[i].setup(modem) == TRUE) {
1107                         ofono_modem_register(modem->modem);
1108                         return FALSE;
1109                 }
1110         }
1111
1112         return TRUE;
1113 }
1114
1115 static void enumerate_devices(struct udev *context)
1116 {
1117         struct udev_enumerate *enumerate;
1118         struct udev_list_entry *entry;
1119
1120         DBG("");
1121
1122         enumerate = udev_enumerate_new(context);
1123         if (enumerate == NULL)
1124                 return;
1125
1126         udev_enumerate_add_match_subsystem(enumerate, "tty");
1127         udev_enumerate_add_match_subsystem(enumerate, "usb");
1128         udev_enumerate_add_match_subsystem(enumerate, "net");
1129
1130         udev_enumerate_scan_devices(enumerate);
1131
1132         entry = udev_enumerate_get_list_entry(enumerate);
1133         while (entry) {
1134                 const char *syspath = udev_list_entry_get_name(entry);
1135                 struct udev_device *device;
1136
1137                 device = udev_device_new_from_syspath(context, syspath);
1138                 if (device != NULL) {
1139                         check_device(device);
1140                         udev_device_unref(device);
1141                 }
1142
1143                 entry = udev_list_entry_get_next(entry);
1144         }
1145
1146         udev_enumerate_unref(enumerate);
1147
1148         g_hash_table_foreach_remove(modem_list, create_modem, NULL);
1149 }
1150
1151 static struct udev *udev_ctx;
1152 static struct udev_monitor *udev_mon;
1153 static guint udev_watch = 0;
1154 static guint udev_delay = 0;
1155
1156 static gboolean check_modem_list(gpointer user_data)
1157 {
1158         udev_delay = 0;
1159
1160         DBG("");
1161
1162         g_hash_table_foreach_remove(modem_list, create_modem, NULL);
1163
1164         return FALSE;
1165 }
1166
1167 static gboolean udev_event(GIOChannel *channel, GIOCondition cond,
1168                                                         gpointer user_data)
1169 {
1170         struct udev_device *device;
1171         const char *action;
1172
1173         if (cond & (G_IO_ERR | G_IO_HUP | G_IO_NVAL)) {
1174                 ofono_warn("Error with udev monitor channel");
1175                 udev_watch = 0;
1176                 return FALSE;
1177         }
1178
1179         device = udev_monitor_receive_device(udev_mon);
1180         if (device == NULL)
1181                 return TRUE;
1182
1183         action = udev_device_get_action(device);
1184         if (action == NULL)
1185                 return TRUE;
1186
1187         if (g_str_equal(action, "add") == TRUE) {
1188                 if (udev_delay > 0)
1189                         g_source_remove(udev_delay);
1190
1191                 check_device(device);
1192
1193                 udev_delay = g_timeout_add_seconds(1, check_modem_list, NULL);
1194         } else if (g_str_equal(action, "remove") == TRUE)
1195                 remove_device(device);
1196
1197         udev_device_unref(device);
1198
1199         return TRUE;
1200 }
1201
1202 static void udev_start(void)
1203 {
1204         GIOChannel *channel;
1205         int fd;
1206
1207         DBG("");
1208
1209         if (udev_monitor_enable_receiving(udev_mon) < 0) {
1210                 ofono_error("Failed to enable udev monitor");
1211                 return;
1212         }
1213
1214         enumerate_devices(udev_ctx);
1215
1216         fd = udev_monitor_get_fd(udev_mon);
1217
1218         channel = g_io_channel_unix_new(fd);
1219         if (channel == NULL)
1220                 return;
1221
1222         udev_watch = g_io_add_watch(channel,
1223                                 G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL,
1224                                                         udev_event, NULL);
1225
1226         g_io_channel_unref(channel);
1227 }
1228
1229 static int detect_init(void)
1230 {
1231         udev_ctx = udev_new();
1232         if (udev_ctx == NULL) {
1233                 ofono_error("Failed to create udev context");
1234                 return -EIO;
1235         }
1236
1237         udev_mon = udev_monitor_new_from_netlink(udev_ctx, "udev");
1238         if (udev_mon == NULL) {
1239                 ofono_error("Failed to create udev monitor");
1240                 udev_unref(udev_ctx);
1241                 udev_ctx = NULL;
1242                 return -EIO;
1243         }
1244
1245         modem_list = g_hash_table_new_full(g_str_hash, g_str_equal,
1246                                                 NULL, destroy_modem);
1247
1248         udev_monitor_filter_add_match_subsystem_devtype(udev_mon, "tty", NULL);
1249         udev_monitor_filter_add_match_subsystem_devtype(udev_mon, "usb", NULL);
1250         udev_monitor_filter_add_match_subsystem_devtype(udev_mon, "net", NULL);
1251
1252         udev_monitor_filter_update(udev_mon);
1253
1254         udev_start();
1255
1256         return 0;
1257 }
1258
1259 static void detect_exit(void)
1260 {
1261         if (udev_delay > 0)
1262                 g_source_remove(udev_delay);
1263
1264         if (udev_watch > 0)
1265                 g_source_remove(udev_watch);
1266
1267         if (udev_ctx == NULL)
1268                 return;
1269
1270         udev_monitor_filter_remove(udev_mon);
1271
1272         g_hash_table_destroy(modem_list);
1273
1274         udev_monitor_unref(udev_mon);
1275         udev_unref(udev_ctx);
1276 }
1277
1278 OFONO_PLUGIN_DEFINE(udevng, "udev hardware detection", VERSION,
1279                 OFONO_PLUGIN_PRIORITY_DEFAULT, detect_init, detect_exit)