3 * oFono - Open Source Telephony
5 * Copyright (C) 2008-2012 Intel Corporation. All rights reserved.
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.
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.
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
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>
48 #include <drivers/qmimodem/qmi.h>
49 #include <drivers/qmimodem/dms.h>
50 #include <drivers/qmimodem/util.h>
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)
64 struct qmi_device *device;
65 struct qmi_service *dms;
66 unsigned long features;
67 unsigned int discover_attempts;
71 static void gobi_debug(const char *str, void *user_data)
73 const char *prefix = user_data;
75 ofono_info("%s%s", prefix, str);
78 static int gobi_probe(struct ofono_modem *modem)
80 struct gobi_data *data;
84 data = g_try_new0(struct gobi_data, 1);
88 ofono_modem_set_data(modem, data);
93 static void gobi_remove(struct ofono_modem *modem)
95 struct gobi_data *data = ofono_modem_get_data(modem);
99 ofono_modem_set_data(modem, NULL);
101 qmi_service_unref(data->dms);
103 qmi_device_unref(data->device);
108 static void shutdown_cb(void *user_data)
110 struct ofono_modem *modem = user_data;
111 struct gobi_data *data = ofono_modem_get_data(modem);
115 data->discover_attempts = 0;
117 qmi_device_unref(data->device);
120 ofono_modem_set_powered(modem, FALSE);
123 static void shutdown_device(struct ofono_modem *modem)
125 struct gobi_data *data = ofono_modem_get_data(modem);
129 qmi_service_unref(data->dms);
132 qmi_device_shutdown(data->device, shutdown_cb, modem, NULL);
135 static void power_reset_cb(struct qmi_result *result, void *user_data)
137 struct ofono_modem *modem = user_data;
141 if (qmi_result_set_error(result, NULL)) {
142 shutdown_device(modem);
146 ofono_modem_set_powered(modem, TRUE);
149 static void get_oper_mode_cb(struct qmi_result *result, void *user_data)
151 struct ofono_modem *modem = user_data;
152 struct gobi_data *data = ofono_modem_get_data(modem);
153 struct qmi_param *param;
158 if (qmi_result_set_error(result, NULL)) {
159 shutdown_device(modem);
163 if (!qmi_result_get_uint8(result, QMI_DMS_RESULT_OPER_MODE, &mode)) {
164 shutdown_device(modem);
168 data->oper_mode = mode;
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);
175 shutdown_device(modem);
179 if (qmi_service_send(data->dms, QMI_DMS_SET_OPER_MODE, param,
180 power_reset_cb, modem, NULL) > 0)
183 shutdown_device(modem);
186 ofono_modem_set_powered(modem, TRUE);
191 static void get_caps_cb(struct qmi_result *result, void *user_data)
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;
201 if (qmi_result_set_error(result, NULL))
204 caps = qmi_result_get(result, QMI_DMS_RESULT_DEVICE_CAPS, &len);
208 DBG("service capabilities %d", caps->data_capa);
209 DBG("sim supported %d", caps->sim_supported);
211 for (i = 0; i < caps->radio_if_count; i++)
212 DBG("radio = %d", caps->radio_if[i]);
214 if (qmi_service_send(data->dms, QMI_DMS_GET_OPER_MODE, NULL,
215 get_oper_mode_cb, modem, NULL) > 0)
219 shutdown_device(modem);
222 static void create_dms_cb(struct qmi_service *service, void *user_data)
224 struct ofono_modem *modem = user_data;
225 struct gobi_data *data = ofono_modem_get_data(modem);
232 data->dms = qmi_service_ref(service);
234 if (qmi_service_send(data->dms, QMI_DMS_GET_CAPS, NULL,
235 get_caps_cb, modem, NULL) > 0)
239 shutdown_device(modem);
242 static void discover_cb(uint8_t count, const struct qmi_version *list,
245 struct ofono_modem *modem = user_data;
246 struct gobi_data *data = ofono_modem_get_data(modem);
251 for (i = 0; i < count; i++) {
252 DBG("%s %d.%d", list[i].name, list[i].major, list[i].minor);
254 switch (list[i].type) {
255 case QMI_SERVICE_DMS:
256 data->features |= GOBI_DMS;
258 case QMI_SERVICE_NAS:
259 data->features |= GOBI_NAS;
261 case QMI_SERVICE_WMS:
262 data->features |= GOBI_WMS;
264 case QMI_SERVICE_WDS:
265 data->features |= GOBI_WDS;
267 case QMI_SERVICE_PDS:
268 data->features |= GOBI_PDS;
270 case QMI_SERVICE_PBM:
271 data->features |= GOBI_PBM;
273 case QMI_SERVICE_UIM:
274 data->features |= GOBI_UIM;
276 case QMI_SERVICE_CAT:
277 data->features |= GOBI_CAT;
279 case QMI_SERVICE_CAT_OLD:
280 if (list[i].major > 0)
281 data->features |= GOBI_CAT_OLD;
283 case QMI_SERVICE_VOICE:
284 data->features |= GOBI_VOICE;
289 if (!(data->features & GOBI_DMS)) {
290 if (++data->discover_attempts < 3) {
291 qmi_device_discover(data->device, discover_cb,
296 shutdown_device(modem);
300 qmi_service_create_shared(data->device, QMI_SERVICE_DMS,
301 create_dms_cb, modem, NULL);
304 static int gobi_enable(struct ofono_modem *modem)
306 struct gobi_data *data = ofono_modem_get_data(modem);
312 device = ofono_modem_get_string(modem, "Device");
316 fd = open(device, O_RDWR | O_NONBLOCK | O_CLOEXEC);
320 data->device = qmi_device_new(fd);
326 if (getenv("OFONO_QMI_DEBUG"))
327 qmi_device_set_debug(data->device, gobi_debug, "QMI: ");
329 qmi_device_set_close_on_unref(data->device, true);
331 qmi_device_discover(data->device, discover_cb, modem, NULL);
336 static void power_disable_cb(struct qmi_result *result, void *user_data)
338 struct ofono_modem *modem = user_data;
342 shutdown_device(modem);
345 static int gobi_disable(struct ofono_modem *modem)
347 struct gobi_data *data = ofono_modem_get_data(modem);
348 struct qmi_param *param;
352 qmi_service_cancel_all(data->dms);
353 qmi_service_unregister_all(data->dms);
355 param = qmi_param_new_uint8(QMI_DMS_PARAM_OPER_MODE,
356 QMI_DMS_OPER_MODE_PERSIST_LOW_POWER);
360 if (qmi_service_send(data->dms, QMI_DMS_SET_OPER_MODE, param,
361 power_disable_cb, modem, NULL) > 0)
364 shutdown_device(modem);
369 static void set_online_cb(struct qmi_result *result, void *user_data)
371 struct cb_data *cbd = user_data;
372 ofono_modem_online_cb_t cb = cbd->cb;
376 if (qmi_result_set_error(result, NULL))
377 CALLBACK_WITH_FAILURE(cb, cbd->data);
379 CALLBACK_WITH_SUCCESS(cb, cbd->data);
382 static void gobi_set_online(struct ofono_modem *modem, ofono_bool_t online,
383 ofono_modem_online_cb_t cb, void *user_data)
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;
390 DBG("%p %s", modem, online ? "online" : "offline");
393 mode = QMI_DMS_OPER_MODE_ONLINE;
395 mode = QMI_DMS_OPER_MODE_LOW_POWER;
397 param = qmi_param_new_uint8(QMI_DMS_PARAM_OPER_MODE, mode);
401 if (qmi_service_send(data->dms, QMI_DMS_SET_OPER_MODE, param,
402 set_online_cb, cbd, g_free) > 0)
405 qmi_param_free(param);
408 CALLBACK_WITH_FAILURE(cb, cbd->data);
413 static void gobi_pre_sim(struct ofono_modem *modem)
415 struct gobi_data *data = ofono_modem_get_data(modem);
419 ofono_devinfo_create(modem, 0, "qmimodem", data->device);
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);
426 if (data->features & GOBI_VOICE)
427 ofono_voicecall_create(modem, 0, "qmimodem", data->device);
429 if (data->features & GOBI_PDS)
430 ofono_location_reporting_create(modem, 0, "qmimodem",
434 static void gobi_post_sim(struct ofono_modem *modem)
436 struct gobi_data *data = ofono_modem_get_data(modem);
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);
445 if (data->features & GOBI_PBM)
446 ofono_phonebook_create(modem, 0, "qmimodem", data->device);
448 if (data->features & GOBI_NAS)
449 ofono_radio_settings_create(modem, 0, "qmimodem", data->device);
451 if (data->features & GOBI_WMS)
452 ofono_sms_create(modem, 0, "qmimodem", data->device);
455 static void gobi_post_online(struct ofono_modem *modem)
457 struct gobi_data *data = ofono_modem_get_data(modem);
458 struct ofono_gprs *gprs;
459 struct ofono_gprs_context *gc;
463 if (data->features & GOBI_NAS)
464 ofono_netreg_create(modem, 0, "qmimodem", data->device);
466 if (data->features & GOBI_VOICE)
467 ofono_ussd_create(modem, 0, "qmimodem", data->device);
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",
475 ofono_gprs_add_context(gprs, gc);
479 static struct ofono_modem_driver gobi_driver = {
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,
491 static int gobi_init(void)
493 return ofono_modem_driver_register(&gobi_driver);
496 static void gobi_exit(void)
498 ofono_modem_driver_unregister(&gobi_driver);
501 OFONO_PLUGIN_DEFINE(gobi, "Qualcomm Gobi modem driver", VERSION,
502 OFONO_PLUGIN_PRIORITY_DEFAULT, gobi_init, gobi_exit)