Upgrade ofono to 1.2
[profile/ivi/ofono.git] / plugins / gobi.c
1 /*
2  *
3  *  oFono - Open Source Telephony
4  *
5  *  Copyright (C) 2008-2012  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 <fcntl.h>
28 #include <unistd.h>
29 #include <stdlib.h>
30
31 #define OFONO_API_SUBJECT_TO_CHANGE
32 #include <ofono/plugin.h>
33 #include <ofono/modem.h>
34 #include <ofono/devinfo.h>
35 #include <ofono/netreg.h>
36 #include <ofono/phonebook.h>
37 #include <ofono/voicecall.h>
38 #include <ofono/sim.h>
39 #include <ofono/stk.h>
40 #include <ofono/sms.h>
41 #include <ofono/ussd.h>
42 #include <ofono/gprs.h>
43 #include <ofono/gprs-context.h>
44 #include <ofono/radio-settings.h>
45 #include <ofono/location-reporting.h>
46 #include <ofono/log.h>
47
48 #include <drivers/qmimodem/qmi.h>
49 #include <drivers/qmimodem/dms.h>
50 #include <drivers/qmimodem/util.h>
51
52 #define GOBI_DMS        (1 << 0)
53 #define GOBI_NAS        (1 << 1)
54 #define GOBI_WMS        (1 << 2)
55 #define GOBI_WDS        (1 << 3)
56 #define GOBI_PDS        (1 << 4)
57 #define GOBI_PBM        (1 << 5)
58 #define GOBI_UIM        (1 << 6)
59 #define GOBI_CAT        (1 << 7)
60 #define GOBI_CAT_OLD    (1 << 8)
61 #define GOBI_VOICE      (1 << 9)
62
63 struct gobi_data {
64         struct qmi_device *device;
65         struct qmi_service *dms;
66         unsigned long features;
67         unsigned int discover_attempts;
68         uint8_t oper_mode;
69 };
70
71 static void gobi_debug(const char *str, void *user_data)
72 {
73         const char *prefix = user_data;
74
75         ofono_info("%s%s", prefix, str);
76 }
77
78 static int gobi_probe(struct ofono_modem *modem)
79 {
80         struct gobi_data *data;
81
82         DBG("%p", modem);
83
84         data = g_try_new0(struct gobi_data, 1);
85         if (!data)
86                 return -ENOMEM;
87
88         ofono_modem_set_data(modem, data);
89
90         return 0;
91 }
92
93 static void gobi_remove(struct ofono_modem *modem)
94 {
95         struct gobi_data *data = ofono_modem_get_data(modem);
96
97         DBG("%p", modem);
98
99         ofono_modem_set_data(modem, NULL);
100
101         qmi_service_unref(data->dms);
102
103         qmi_device_unref(data->device);
104
105         g_free(data);
106 }
107
108 static void shutdown_cb(void *user_data)
109 {
110         struct ofono_modem *modem = user_data;
111         struct gobi_data *data = ofono_modem_get_data(modem);
112
113         DBG("");
114
115         data->discover_attempts = 0;
116
117         qmi_device_unref(data->device);
118         data->device = NULL;
119
120         ofono_modem_set_powered(modem, FALSE);
121 }
122
123 static void shutdown_device(struct ofono_modem *modem)
124 {
125         struct gobi_data *data = ofono_modem_get_data(modem);
126
127         DBG("%p", modem);
128
129         qmi_service_unref(data->dms);
130         data->dms = NULL;
131
132         qmi_device_shutdown(data->device, shutdown_cb, modem, NULL);
133 }
134
135 static void power_reset_cb(struct qmi_result *result, void *user_data)
136 {
137         struct ofono_modem *modem = user_data;
138
139         DBG("");
140
141         if (qmi_result_set_error(result, NULL)) {
142                 shutdown_device(modem);
143                 return;
144         }
145
146         ofono_modem_set_powered(modem, TRUE);
147 }
148
149 static void get_oper_mode_cb(struct qmi_result *result, void *user_data)
150 {
151         struct ofono_modem *modem = user_data;
152         struct gobi_data *data = ofono_modem_get_data(modem);
153         struct qmi_param *param;
154         uint8_t mode;
155
156         DBG("");
157
158         if (qmi_result_set_error(result, NULL)) {
159                 shutdown_device(modem);
160                 return;
161         }
162
163         if (!qmi_result_get_uint8(result, QMI_DMS_RESULT_OPER_MODE, &mode)) {
164                 shutdown_device(modem);
165                 return;
166         }
167
168         data->oper_mode = mode;
169
170         switch (data->oper_mode) {
171         case QMI_DMS_OPER_MODE_ONLINE:
172                 param = qmi_param_new_uint8(QMI_DMS_PARAM_OPER_MODE,
173                                         QMI_DMS_OPER_MODE_PERSIST_LOW_POWER);
174                 if (!param) {
175                         shutdown_device(modem);
176                         return;
177                 }
178
179                 if (qmi_service_send(data->dms, QMI_DMS_SET_OPER_MODE, param,
180                                         power_reset_cb, modem, NULL) > 0)
181                         return;
182
183                 shutdown_device(modem);
184                 break;
185         default:
186                 ofono_modem_set_powered(modem, TRUE);
187                 break;
188         }
189 }
190
191 static void get_caps_cb(struct qmi_result *result, void *user_data)
192 {
193         struct ofono_modem *modem = user_data;
194         struct gobi_data *data = ofono_modem_get_data(modem);
195         const struct qmi_dms_device_caps *caps;
196         uint16_t len;
197         uint8_t i;
198
199         DBG("");
200
201         if (qmi_result_set_error(result, NULL))
202                 goto error;
203
204         caps = qmi_result_get(result, QMI_DMS_RESULT_DEVICE_CAPS, &len);
205         if (!caps)
206                 goto error;
207
208         DBG("service capabilities %d", caps->data_capa);
209         DBG("sim supported %d", caps->sim_supported);
210
211         for (i = 0; i < caps->radio_if_count; i++)
212                 DBG("radio = %d", caps->radio_if[i]);
213
214         if (qmi_service_send(data->dms, QMI_DMS_GET_OPER_MODE, NULL,
215                                         get_oper_mode_cb, modem, NULL) > 0)
216                 return;
217
218 error:
219         shutdown_device(modem);
220 }
221
222 static void create_dms_cb(struct qmi_service *service, void *user_data)
223 {
224         struct ofono_modem *modem = user_data;
225         struct gobi_data *data = ofono_modem_get_data(modem);
226
227         DBG("");
228
229         if (!service)
230                 goto error;
231
232         data->dms = qmi_service_ref(service);
233
234         if (qmi_service_send(data->dms, QMI_DMS_GET_CAPS, NULL,
235                                         get_caps_cb, modem, NULL) > 0)
236                 return;
237
238 error:
239         shutdown_device(modem);
240 }
241
242 static void discover_cb(uint8_t count, const struct qmi_version *list,
243                                                         void *user_data)
244 {
245         struct ofono_modem *modem = user_data;
246         struct gobi_data *data = ofono_modem_get_data(modem);
247         uint8_t i;
248
249         DBG("");
250
251         for (i = 0; i < count; i++) {
252                 DBG("%s %d.%d", list[i].name, list[i].major, list[i].minor);
253
254                 switch (list[i].type) {
255                 case QMI_SERVICE_DMS:
256                         data->features |= GOBI_DMS;
257                         break;
258                 case QMI_SERVICE_NAS:
259                         data->features |= GOBI_NAS;
260                         break;
261                 case QMI_SERVICE_WMS:
262                         data->features |= GOBI_WMS;
263                         break;
264                 case QMI_SERVICE_WDS:
265                         data->features |= GOBI_WDS;
266                         break;
267                 case QMI_SERVICE_PDS:
268                         data->features |= GOBI_PDS;
269                         break;
270                 case QMI_SERVICE_PBM:
271                         data->features |= GOBI_PBM;
272                         break;
273                 case QMI_SERVICE_UIM:
274                         data->features |= GOBI_UIM;
275                         break;
276                 case QMI_SERVICE_CAT:
277                         data->features |= GOBI_CAT;
278                         break;
279                 case QMI_SERVICE_CAT_OLD:
280                         if (list[i].major > 0)
281                                 data->features |= GOBI_CAT_OLD;
282                         break;
283                 case QMI_SERVICE_VOICE:
284                         data->features |= GOBI_VOICE;
285                         break;
286                 }
287         }
288
289         if (!(data->features & GOBI_DMS)) {
290                 if (++data->discover_attempts < 3) {
291                         qmi_device_discover(data->device, discover_cb,
292                                                                 modem, NULL);
293                         return;
294                 }
295
296                 shutdown_device(modem);
297                 return;
298         }
299
300         qmi_service_create_shared(data->device, QMI_SERVICE_DMS,
301                                                 create_dms_cb, modem, NULL);
302 }
303
304 static int gobi_enable(struct ofono_modem *modem)
305 {
306         struct gobi_data *data = ofono_modem_get_data(modem);
307         const char *device;
308         int fd;
309
310         DBG("%p", modem);
311
312         device = ofono_modem_get_string(modem, "Device");
313         if (!device)
314                 return -EINVAL;
315
316         fd = open(device, O_RDWR | O_NONBLOCK | O_CLOEXEC);
317         if (fd < 0)
318                 return -EIO;
319
320         data->device = qmi_device_new(fd);
321         if (!data->device) {
322                 close(fd);
323                 return -ENOMEM;
324         }
325
326         if (getenv("OFONO_QMI_DEBUG"))
327                 qmi_device_set_debug(data->device, gobi_debug, "QMI: ");
328
329         qmi_device_set_close_on_unref(data->device, true);
330
331         qmi_device_discover(data->device, discover_cb, modem, NULL);
332
333         return -EINPROGRESS;
334 }
335
336 static void power_disable_cb(struct qmi_result *result, void *user_data)
337 {
338         struct ofono_modem *modem = user_data;
339
340         DBG("");
341
342         shutdown_device(modem);
343 }
344
345 static int gobi_disable(struct ofono_modem *modem)
346 {
347         struct gobi_data *data = ofono_modem_get_data(modem);
348         struct qmi_param *param;
349
350         DBG("%p", modem);
351
352         qmi_service_cancel_all(data->dms);
353         qmi_service_unregister_all(data->dms);
354
355         param = qmi_param_new_uint8(QMI_DMS_PARAM_OPER_MODE,
356                                         QMI_DMS_OPER_MODE_PERSIST_LOW_POWER);
357         if (!param)
358                 return -ENOMEM;
359
360         if (qmi_service_send(data->dms, QMI_DMS_SET_OPER_MODE, param,
361                                         power_disable_cb, modem, NULL) > 0)
362                 return -EINPROGRESS;
363
364         shutdown_device(modem);
365
366         return -EINPROGRESS;
367 }
368
369 static void set_online_cb(struct qmi_result *result, void *user_data)
370 {
371         struct cb_data *cbd = user_data;
372         ofono_modem_online_cb_t cb = cbd->cb;
373
374         DBG("");
375
376         if (qmi_result_set_error(result, NULL))
377                 CALLBACK_WITH_FAILURE(cb, cbd->data);
378         else
379                 CALLBACK_WITH_SUCCESS(cb, cbd->data);
380 }
381
382 static void gobi_set_online(struct ofono_modem *modem, ofono_bool_t online,
383                                 ofono_modem_online_cb_t cb, void *user_data)
384 {
385         struct gobi_data *data = ofono_modem_get_data(modem);
386         struct cb_data *cbd = cb_data_new(cb, user_data);
387         struct qmi_param *param;
388         uint8_t mode;
389
390         DBG("%p %s", modem, online ? "online" : "offline");
391
392         if (online)
393                 mode = QMI_DMS_OPER_MODE_ONLINE;
394         else
395                 mode = QMI_DMS_OPER_MODE_LOW_POWER;
396
397         param = qmi_param_new_uint8(QMI_DMS_PARAM_OPER_MODE, mode);
398         if (!param)
399                 goto error;
400
401         if (qmi_service_send(data->dms, QMI_DMS_SET_OPER_MODE, param,
402                                         set_online_cb, cbd, g_free) > 0)
403                 return;
404
405         qmi_param_free(param);
406
407 error:
408         CALLBACK_WITH_FAILURE(cb, cbd->data);
409
410         g_free(cbd);
411 }
412
413 static void gobi_pre_sim(struct ofono_modem *modem)
414 {
415         struct gobi_data *data = ofono_modem_get_data(modem);
416
417         DBG("%p", modem);
418
419         ofono_devinfo_create(modem, 0, "qmimodem", data->device);
420
421         if (data->features & GOBI_UIM)
422                 ofono_sim_create(modem, 0, "qmimodem", data->device);
423         else if (data->features & GOBI_DMS)
424                 ofono_sim_create(modem, 0, "qmimodem-legacy", data->device);
425
426         if (data->features & GOBI_VOICE)
427                 ofono_voicecall_create(modem, 0, "qmimodem", data->device);
428
429         if (data->features & GOBI_PDS)
430                 ofono_location_reporting_create(modem, 0, "qmimodem",
431                                                         data->device);
432 }
433
434 static void gobi_post_sim(struct ofono_modem *modem)
435 {
436         struct gobi_data *data = ofono_modem_get_data(modem);
437
438         DBG("%p", modem);
439
440         if (data->features & GOBI_CAT)
441                 ofono_stk_create(modem, 0, "qmimodem", data->device);
442         else if (data->features & GOBI_CAT_OLD)
443                 ofono_stk_create(modem, 1, "qmimodem", data->device);
444
445         if (data->features & GOBI_PBM)
446                 ofono_phonebook_create(modem, 0, "qmimodem", data->device);
447
448         if (data->features & GOBI_NAS)
449                 ofono_radio_settings_create(modem, 0, "qmimodem", data->device);
450
451         if (data->features & GOBI_WMS)
452                 ofono_sms_create(modem, 0, "qmimodem", data->device);
453 }
454
455 static void gobi_post_online(struct ofono_modem *modem)
456 {
457         struct gobi_data *data = ofono_modem_get_data(modem);
458         struct ofono_gprs *gprs;
459         struct ofono_gprs_context *gc;
460
461         DBG("%p", modem);
462
463         if (data->features & GOBI_NAS)
464                 ofono_netreg_create(modem, 0, "qmimodem", data->device);
465
466         if (data->features & GOBI_VOICE)
467                 ofono_ussd_create(modem, 0, "qmimodem", data->device);
468
469         if (data->features & GOBI_WDS) {
470                 gprs = ofono_gprs_create(modem, 0, "qmimodem", data->device);
471                 gc = ofono_gprs_context_create(modem, 0, "qmimodem",
472                                                         data->device);
473
474                 if (gprs && gc)
475                         ofono_gprs_add_context(gprs, gc);
476         }
477 }
478
479 static struct ofono_modem_driver gobi_driver = {
480         .name           = "gobi",
481         .probe          = gobi_probe,
482         .remove         = gobi_remove,
483         .enable         = gobi_enable,
484         .disable        = gobi_disable,
485         .set_online     = gobi_set_online,
486         .pre_sim        = gobi_pre_sim,
487         .post_sim       = gobi_post_sim,
488         .post_online    = gobi_post_online,
489 };
490
491 static int gobi_init(void)
492 {
493         return ofono_modem_driver_register(&gobi_driver);
494 }
495
496 static void gobi_exit(void)
497 {
498         ofono_modem_driver_unregister(&gobi_driver);
499 }
500
501 OFONO_PLUGIN_DEFINE(gobi, "Qualcomm Gobi modem driver", VERSION,
502                         OFONO_PLUGIN_PRIORITY_DEFAULT, gobi_init, gobi_exit)