5924b1633fdaa251487528336f324a8ffd90a531
[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_CAT:
274                         data->features |= GOBI_CAT;
275                         break;
276                 case QMI_SERVICE_CAT_OLD:
277                         if (list[i].major > 0)
278                                 data->features |= GOBI_CAT_OLD;
279                         break;
280                 case QMI_SERVICE_VOICE:
281                         data->features |= GOBI_VOICE;
282                         break;
283                 }
284         }
285
286         if (!(data->features & GOBI_DMS)) {
287                 if (++data->discover_attempts < 3) {
288                         qmi_device_discover(data->device, discover_cb,
289                                                                 modem, NULL);
290                         return;
291                 }
292
293                 shutdown_device(modem);
294                 return;
295         }
296
297         qmi_service_create_shared(data->device, QMI_SERVICE_DMS,
298                                                 create_dms_cb, modem, NULL);
299 }
300
301 static int gobi_enable(struct ofono_modem *modem)
302 {
303         struct gobi_data *data = ofono_modem_get_data(modem);
304         const char *device;
305         int fd;
306
307         DBG("%p", modem);
308
309         device = ofono_modem_get_string(modem, "Device");
310         if (!device)
311                 return -EINVAL;
312
313         fd = open(device, O_RDWR | O_NONBLOCK | O_CLOEXEC);
314         if (fd < 0)
315                 return -EIO;
316
317         data->device = qmi_device_new(fd);
318         if (!data->device) {
319                 close(fd);
320                 return -ENOMEM;
321         }
322
323         if (getenv("OFONO_QMI_DEBUG"))
324                 qmi_device_set_debug(data->device, gobi_debug, "QMI: ");
325
326         qmi_device_set_close_on_unref(data->device, true);
327
328         qmi_device_discover(data->device, discover_cb, modem, NULL);
329
330         return -EINPROGRESS;
331 }
332
333 static void power_disable_cb(struct qmi_result *result, void *user_data)
334 {
335         struct ofono_modem *modem = user_data;
336
337         DBG("");
338
339         shutdown_device(modem);
340 }
341
342 static int gobi_disable(struct ofono_modem *modem)
343 {
344         struct gobi_data *data = ofono_modem_get_data(modem);
345         struct qmi_param *param;
346
347         DBG("%p", modem);
348
349         qmi_service_cancel_all(data->dms);
350         qmi_service_unregister_all(data->dms);
351
352         param = qmi_param_new_uint8(QMI_DMS_PARAM_OPER_MODE,
353                                         QMI_DMS_OPER_MODE_PERSIST_LOW_POWER);
354         if (!param)
355                 return -ENOMEM;
356
357         if (qmi_service_send(data->dms, QMI_DMS_SET_OPER_MODE, param,
358                                         power_disable_cb, modem, NULL) > 0)
359                 return -EINPROGRESS;
360
361         shutdown_device(modem);
362
363         return -EINPROGRESS;
364 }
365
366 static void set_online_cb(struct qmi_result *result, void *user_data)
367 {
368         struct cb_data *cbd = user_data;
369         ofono_modem_online_cb_t cb = cbd->cb;
370
371         DBG("");
372
373         if (qmi_result_set_error(result, NULL))
374                 CALLBACK_WITH_FAILURE(cb, cbd->data);
375         else
376                 CALLBACK_WITH_SUCCESS(cb, cbd->data);
377 }
378
379 static void gobi_set_online(struct ofono_modem *modem, ofono_bool_t online,
380                                 ofono_modem_online_cb_t cb, void *user_data)
381 {
382         struct gobi_data *data = ofono_modem_get_data(modem);
383         struct cb_data *cbd = cb_data_new(cb, user_data);
384         struct qmi_param *param;
385         uint8_t mode;
386
387         DBG("%p %s", modem, online ? "online" : "offline");
388
389         if (online)
390                 mode = QMI_DMS_OPER_MODE_ONLINE;
391         else
392                 mode = QMI_DMS_OPER_MODE_LOW_POWER;
393
394         param = qmi_param_new_uint8(QMI_DMS_PARAM_OPER_MODE, mode);
395         if (!param)
396                 goto error;
397
398         if (qmi_service_send(data->dms, QMI_DMS_SET_OPER_MODE, param,
399                                         set_online_cb, cbd, g_free) > 0)
400                 return;
401
402         qmi_param_free(param);
403
404 error:
405         CALLBACK_WITH_FAILURE(cb, cbd->data);
406
407         g_free(cbd);
408 }
409
410 static void gobi_pre_sim(struct ofono_modem *modem)
411 {
412         struct gobi_data *data = ofono_modem_get_data(modem);
413
414         DBG("%p", modem);
415
416         ofono_devinfo_create(modem, 0, "qmimodem", data->device);
417
418         if (data->features & GOBI_UIM)
419                 ofono_sim_create(modem, 0, "qmimodem", data->device);
420         else if (data->features & GOBI_DMS)
421                 ofono_sim_create(modem, 0, "qmimodem-legacy", data->device);
422
423         if (data->features & GOBI_VOICE)
424                 ofono_voicecall_create(modem, 0, "qmimodem", data->device);
425
426         if (data->features & GOBI_PDS)
427                 ofono_location_reporting_create(modem, 0, "qmimodem",
428                                                         data->device);
429 }
430
431 static void gobi_post_sim(struct ofono_modem *modem)
432 {
433         struct gobi_data *data = ofono_modem_get_data(modem);
434
435         DBG("%p", modem);
436
437         if (data->features & GOBI_CAT)
438                 ofono_stk_create(modem, 0, "qmimodem", data->device);
439         else if (data->features & GOBI_CAT_OLD)
440                 ofono_stk_create(modem, 1, "qmimodem", data->device);
441
442         if (data->features & GOBI_PBM)
443                 ofono_phonebook_create(modem, 0, "qmimodem", data->device);
444
445         if (data->features & GOBI_NAS)
446                 ofono_radio_settings_create(modem, 0, "qmimodem", data->device);
447
448         if (data->features & GOBI_WMS)
449                 ofono_sms_create(modem, 0, "qmimodem", data->device);
450 }
451
452 static void gobi_post_online(struct ofono_modem *modem)
453 {
454         struct gobi_data *data = ofono_modem_get_data(modem);
455         struct ofono_gprs *gprs;
456         struct ofono_gprs_context *gc;
457
458         DBG("%p", modem);
459
460         if (data->features & GOBI_NAS)
461                 ofono_netreg_create(modem, 0, "qmimodem", data->device);
462
463         if (data->features & GOBI_VOICE)
464                 ofono_ussd_create(modem, 0, "qmimodem", data->device);
465
466         if (data->features & GOBI_WDS) {
467                 gprs = ofono_gprs_create(modem, 0, "qmimodem", data->device);
468                 gc = ofono_gprs_context_create(modem, 0, "qmimodem",
469                                                         data->device);
470
471                 if (gprs && gc)
472                         ofono_gprs_add_context(gprs, gc);
473         }
474 }
475
476 static struct ofono_modem_driver gobi_driver = {
477         .name           = "gobi",
478         .probe          = gobi_probe,
479         .remove         = gobi_remove,
480         .enable         = gobi_enable,
481         .disable        = gobi_disable,
482         .set_online     = gobi_set_online,
483         .pre_sim        = gobi_pre_sim,
484         .post_sim       = gobi_post_sim,
485         .post_online    = gobi_post_online,
486 };
487
488 static int gobi_init(void)
489 {
490         return ofono_modem_driver_register(&gobi_driver);
491 }
492
493 static void gobi_exit(void)
494 {
495         ofono_modem_driver_unregister(&gobi_driver);
496 }
497
498 OFONO_PLUGIN_DEFINE(gobi, "Qualcomm Gobi modem driver", VERSION,
499                         OFONO_PLUGIN_PRIORITY_DEFAULT, gobi_init, gobi_exit)