summaryrefslogtreecommitdiffstats
path: root/plugins/gobi.c
diff options
context:
space:
mode:
authorMarcel Holtmann <marcel@holtmann.org>2012-06-21 16:09:57 -0700
committerMarcel Holtmann <marcel@holtmann.org>2012-06-21 16:09:57 -0700
commitbe4423e523a9b25468c7c3063d63cba71594832a (patch)
treeedd96420a7187a497f76e57d5261bc61e62a8657 /plugins/gobi.c
parentad20ea12c568c7d3d883867c80c670708b85d3e2 (diff)
downloadofono-be4423e523a9b25468c7c3063d63cba71594832a.tar.bz2
gobi: Use QMI support to drive this modem
Diffstat (limited to 'plugins/gobi.c')
-rw-r--r--plugins/gobi.c373
1 files changed, 254 insertions, 119 deletions
diff --git a/plugins/gobi.c b/plugins/gobi.c
index 08e8aa6b..c0879b09 100644
--- a/plugins/gobi.c
+++ b/plugins/gobi.c
@@ -2,7 +2,7 @@
*
* oFono - Open Source Telephony
*
- * Copyright (C) 2008-2011 Intel Corporation. All rights reserved.
+ * Copyright (C) 2008-2012 Intel Corporation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
@@ -24,32 +24,44 @@
#endif
#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
#include <stdlib.h>
-#include <glib.h>
-#include <gatchat.h>
-#include <gattty.h>
-
#define OFONO_API_SUBJECT_TO_CHANGE
#include <ofono/plugin.h>
-#include <ofono/log.h>
#include <ofono/modem.h>
#include <ofono/devinfo.h>
#include <ofono/netreg.h>
+#include <ofono/phonebook.h>
#include <ofono/sim.h>
+#include <ofono/stk.h>
+#include <ofono/sms.h>
#include <ofono/gprs.h>
#include <ofono/gprs-context.h>
+#include <ofono/location-reporting.h>
+#include <ofono/log.h>
-#include <drivers/atmodem/atutil.h>
-#include <drivers/atmodem/vendor.h>
+#include <drivers/qmimodem/qmi.h>
+#include <drivers/qmimodem/dms.h>
+#include <drivers/qmimodem/util.h>
-static const char *none_prefix[] = { NULL };
+#define GOBI_DMS (1 << 0)
+#define GOBI_NAS (1 << 1)
+#define GOBI_WMS (1 << 2)
+#define GOBI_WDS (1 << 3)
+#define GOBI_PDS (1 << 4)
+#define GOBI_PBM (1 << 5)
+#define GOBI_UIM (1 << 6)
+#define GOBI_CAT (1 << 7)
+#define GOBI_CAT_OLD (1 << 8)
struct gobi_data {
- GAtChat *chat;
- struct ofono_sim *sim;
- gboolean have_sim;
- guint cfun_timeout;
+ struct qmi_device *device;
+ struct qmi_service *dms;
+ unsigned long features;
+ unsigned int discover_attempts;
+ uint8_t oper_mode;
};
static void gobi_debug(const char *str, void *user_data)
@@ -66,7 +78,7 @@ static int gobi_probe(struct ofono_modem *modem)
DBG("%p", modem);
data = g_try_new0(struct gobi_data, 1);
- if (data == NULL)
+ if (!data)
return -ENOMEM;
ofono_modem_set_data(modem, data);
@@ -82,191 +94,279 @@ static void gobi_remove(struct ofono_modem *modem)
ofono_modem_set_data(modem, NULL);
- /* Cleanup after hot-unplug */
- g_at_chat_unref(data->chat);
+ qmi_service_unref(data->dms);
+
+ qmi_device_unref(data->device);
g_free(data);
}
-static GAtChat *open_device(struct ofono_modem *modem,
- const char *key, char *debug)
+static void shutdown_cb(void *user_data)
{
- const char *device;
- GIOChannel *channel;
- GAtSyntax *syntax;
- GAtChat *chat;
+ struct ofono_modem *modem = user_data;
+ struct gobi_data *data = ofono_modem_get_data(modem);
- device = ofono_modem_get_string(modem, key);
- if (device == NULL)
- return NULL;
+ DBG("");
- DBG("%s %s", key, device);
+ data->discover_attempts = 0;
- channel = g_at_tty_open(device, NULL);
- if (channel == NULL)
- return NULL;
+ qmi_device_unref(data->device);
+ data->device = NULL;
- syntax = g_at_syntax_new_gsm_permissive();
- chat = g_at_chat_new(channel, syntax);
- g_at_syntax_unref(syntax);
+ ofono_modem_set_powered(modem, FALSE);
+}
- g_io_channel_unref(channel);
+static void shutdown_device(struct ofono_modem *modem)
+{
+ struct gobi_data *data = ofono_modem_get_data(modem);
- if (chat == NULL)
- return NULL;
+ DBG("%p", modem);
- if (getenv("OFONO_AT_DEBUG"))
- g_at_chat_set_debug(chat, gobi_debug, debug);
+ qmi_service_unref(data->dms);
+ data->dms = NULL;
- return chat;
+ qmi_device_shutdown(data->device, shutdown_cb, modem, NULL);
}
-static void simstat_notify(GAtResult *result, gpointer user_data)
+static void power_reset_cb(struct qmi_result *result, void *user_data)
{
struct ofono_modem *modem = user_data;
- struct gobi_data *data = ofono_modem_get_data(modem);
- GAtResultIter iter;
- const char *state, *tmp;
- if (data->sim == NULL)
+ DBG("");
+
+ if (qmi_result_set_error(result, NULL)) {
+ shutdown_device(modem);
return;
+ }
- g_at_result_iter_init(&iter, result);
+ ofono_modem_set_powered(modem, TRUE);
+}
- if (!g_at_result_iter_next(&iter, "$QCSIMSTAT:"))
- return;
+static void get_oper_mode_cb(struct qmi_result *result, void *user_data)
+{
+ struct ofono_modem *modem = user_data;
+ struct gobi_data *data = ofono_modem_get_data(modem);
+ struct qmi_param *param;
+ uint8_t mode;
- if (!g_at_result_iter_next_unquoted_string(&iter, &tmp))
+ DBG("");
+
+ if (qmi_result_set_error(result, NULL)) {
+ shutdown_device(modem);
return;
+ }
- /*
- * When receiving an unsolicited notification, the comma
- * is missing ($QCSIMSTAT: 1 SIM INIT COMPLETED). Handle
- * this gracefully.
- */
- if (g_str_has_prefix(tmp, "1 ") == TRUE)
- state = tmp + 2;
- else if (!g_at_result_iter_next_unquoted_string(&iter, &state))
+ if (!qmi_result_get_uint8(result, QMI_DMS_RESULT_OPER_MODE, &mode)) {
+ shutdown_device(modem);
return;
+ }
- DBG("state %s", state);
+ data->oper_mode = mode;
- if (g_str_equal(state, "SIM INIT COMPLETED") == TRUE) {
- if (data->have_sim == FALSE) {
- ofono_sim_inserted_notify(data->sim, TRUE);
- data->have_sim = TRUE;
+ switch (data->oper_mode) {
+ case QMI_DMS_OPER_MODE_ONLINE:
+ param = qmi_param_new_uint8(QMI_DMS_PARAM_OPER_MODE,
+ QMI_DMS_OPER_MODE_PERSIST_LOW_POWER);
+ if (!param) {
+ shutdown_device(modem);
+ return;
}
+
+ if (qmi_service_send(data->dms, QMI_DMS_SET_OPER_MODE, param,
+ power_reset_cb, modem, NULL) > 0)
+ return;
+
+ shutdown_device(modem);
+ break;
+ default:
+ ofono_modem_set_powered(modem, TRUE);
+ break;
}
}
-static void cfun_enable(gboolean ok, GAtResult *result, gpointer user_data)
+static void get_caps_cb(struct qmi_result *result, void *user_data)
{
struct ofono_modem *modem = user_data;
struct gobi_data *data = ofono_modem_get_data(modem);
+ const struct qmi_dms_device_caps *caps;
+ uint16_t len;
+ uint8_t i;
DBG("");
- if (data->cfun_timeout > 0) {
- g_source_remove(data->cfun_timeout);
- data->cfun_timeout = 0;
- }
+ if (qmi_result_set_error(result, NULL))
+ goto error;
+
+ caps = qmi_result_get(result, QMI_DMS_RESULT_DEVICE_CAPS, &len);
+ if (!caps)
+ goto error;
- if (!ok) {
- g_at_chat_unref(data->chat);
- data->chat = NULL;
+ DBG("service capabilities %d", caps->data_capa);
+ DBG("sim supported %d", caps->sim_supported);
- ofono_modem_set_powered(modem, FALSE);
+ for (i = 0; i < caps->radio_if_count; i++)
+ DBG("radio = %d", caps->radio_if[i]);
+
+ if (qmi_service_send(data->dms, QMI_DMS_GET_OPER_MODE, NULL,
+ get_oper_mode_cb, modem, NULL) > 0)
return;
- }
- data->have_sim = FALSE;
+error:
+ shutdown_device(modem);
+}
- g_at_chat_register(data->chat, "$QCSIMSTAT:", simstat_notify,
- FALSE, modem, NULL);
+static void create_dms_cb(struct qmi_service *service, void *user_data)
+{
+ struct ofono_modem *modem = user_data;
+ struct gobi_data *data = ofono_modem_get_data(modem);
- g_at_chat_send(data->chat, "AT$QCSIMSTAT=1", none_prefix,
- NULL, NULL, NULL);
+ DBG("");
- g_at_chat_send(data->chat, "AT$QCSIMSTAT?", none_prefix,
- NULL, NULL, NULL);
+ if (!service)
+ goto error;
- ofono_modem_set_powered(modem, TRUE);
+ data->dms = qmi_service_ref(service);
+
+ if (qmi_service_send(data->dms, QMI_DMS_GET_CAPS, NULL,
+ get_caps_cb, modem, NULL) > 0)
+ return;
+
+error:
+ shutdown_device(modem);
}
-static gboolean cfun_timeout(gpointer user_data)
+static void discover_cb(uint8_t count, const struct qmi_version *list,
+ void *user_data)
{
struct ofono_modem *modem = user_data;
struct gobi_data *data = ofono_modem_get_data(modem);
+ uint8_t i;
- ofono_error("Modem enabling timeout, RFKILL enabled?");
+ DBG("");
- data->cfun_timeout = 0;
+ for (i = 0; i < count; i++) {
+ DBG("%s %d.%d", list[i].name, list[i].major, list[i].minor);
+
+ switch (list[i].type) {
+ case QMI_SERVICE_DMS:
+ data->features |= GOBI_DMS;
+ break;
+ case QMI_SERVICE_NAS:
+ data->features |= GOBI_NAS;
+ break;
+ case QMI_SERVICE_WMS:
+ data->features |= GOBI_WMS;
+ break;
+ case QMI_SERVICE_WDS:
+ data->features |= GOBI_WDS;
+ break;
+ case QMI_SERVICE_PDS:
+ data->features |= GOBI_PDS;
+ break;
+ case QMI_SERVICE_PBM:
+ data->features |= GOBI_PBM;
+ break;
+ case QMI_SERVICE_UIM:
+ data->features |= GOBI_UIM;
+ break;
+ case QMI_SERVICE_CAT:
+ data->features |= GOBI_CAT;
+ break;
+ case QMI_SERVICE_CAT_OLD:
+ if (list[i].major > 0)
+ data->features |= GOBI_CAT_OLD;
+ break;
+ }
+ }
- g_at_chat_unref(data->chat);
- data->chat = NULL;
+ if (!(data->features & GOBI_DMS)) {
+ if (++data->discover_attempts < 3) {
+ qmi_device_discover(data->device, discover_cb,
+ modem, NULL);
+ return;
+ }
- ofono_modem_set_powered(modem, FALSE);
+ shutdown_device(modem);
+ return;
+ }
- return FALSE;
+ qmi_service_create(data->device, QMI_SERVICE_DMS,
+ create_dms_cb, modem, NULL);
}
static int gobi_enable(struct ofono_modem *modem)
{
struct gobi_data *data = ofono_modem_get_data(modem);
+ const char *device;
+ int fd;
DBG("%p", modem);
- data->chat = open_device(modem, "Modem", "Modem: ");
- if (data->chat == NULL)
+ device = ofono_modem_get_string(modem, "Device");
+ if (!device)
return -EINVAL;
- g_at_chat_send(data->chat, "ATE0 +CMEE=1", NULL, NULL, NULL, NULL);
+ fd = open(device, O_RDWR | O_NONBLOCK | O_CLOEXEC);
+ if (fd < 0)
+ return -EIO;
- g_at_chat_send(data->chat, "AT+CFUN=4", none_prefix,
- cfun_enable, modem, NULL);
+ data->device = qmi_device_new(fd);
+ if (!data->device) {
+ close(fd);
+ return -ENOMEM;
+ }
+
+ if (getenv("OFONO_QMI_DEBUG"))
+ qmi_device_set_debug(data->device, gobi_debug, "QMI: ");
- data->cfun_timeout = g_timeout_add_seconds(5, cfun_timeout, modem);
+ qmi_device_set_close_on_unref(data->device, true);
+
+ qmi_device_discover(data->device, discover_cb, modem, NULL);
return -EINPROGRESS;
}
-static void cfun_disable(gboolean ok, GAtResult *result, gpointer user_data)
+static void power_disable_cb(struct qmi_result *result, void *user_data)
{
struct ofono_modem *modem = user_data;
- struct gobi_data *data = ofono_modem_get_data(modem);
DBG("");
- g_at_chat_unref(data->chat);
- data->chat = NULL;
-
- if (ok)
- ofono_modem_set_powered(modem, FALSE);
+ shutdown_device(modem);
}
static int gobi_disable(struct ofono_modem *modem)
{
struct gobi_data *data = ofono_modem_get_data(modem);
+ struct qmi_param *param;
DBG("%p", modem);
- g_at_chat_cancel_all(data->chat);
- g_at_chat_unregister_all(data->chat);
+ param = qmi_param_new_uint8(QMI_DMS_PARAM_OPER_MODE,
+ QMI_DMS_OPER_MODE_PERSIST_LOW_POWER);
+ if (!param)
+ return -ENOMEM;
+
+ if (qmi_service_send(data->dms, QMI_DMS_SET_OPER_MODE, param,
+ power_disable_cb, modem, NULL) > 0)
+ return -EINPROGRESS;
- g_at_chat_send(data->chat, "AT+CFUN=0", none_prefix,
- cfun_disable, modem, NULL);
+ shutdown_device(modem);
return -EINPROGRESS;
}
-static void set_online_cb(gboolean ok, GAtResult *result, gpointer user_data)
+static void set_online_cb(struct qmi_result *result, void *user_data)
{
struct cb_data *cbd = user_data;
ofono_modem_online_cb_t cb = cbd->cb;
- struct ofono_error error;
- decode_at_error(&error, g_at_result_final_response(result));
- cb(&error, cbd->data);
+ DBG("");
+
+ if (qmi_result_set_error(result, NULL))
+ CALLBACK_WITH_FAILURE(cb, cbd->data);
+ else
+ CALLBACK_WITH_SUCCESS(cb, cbd->data);
}
static void gobi_set_online(struct ofono_modem *modem, ofono_bool_t online,
@@ -274,14 +374,27 @@ static void gobi_set_online(struct ofono_modem *modem, ofono_bool_t online,
{
struct gobi_data *data = ofono_modem_get_data(modem);
struct cb_data *cbd = cb_data_new(cb, user_data);
- char const *command = online ? "AT+CFUN=1" : "AT+CFUN=4";
+ struct qmi_param *param;
+ uint8_t mode;
- DBG("modem %p %s", modem, online ? "online" : "offline");
+ DBG("%p %s", modem, online ? "online" : "offline");
- if (g_at_chat_send(data->chat, command, none_prefix,
+ if (online)
+ mode = QMI_DMS_OPER_MODE_ONLINE;
+ else
+ mode = QMI_DMS_OPER_MODE_LOW_POWER;
+
+ param = qmi_param_new_uint8(QMI_DMS_PARAM_OPER_MODE, mode);
+ if (!param)
+ goto error;
+
+ if (qmi_service_send(data->dms, QMI_DMS_SET_OPER_MODE, param,
set_online_cb, cbd, g_free) > 0)
return;
+ qmi_param_free(param);
+
+error:
CALLBACK_WITH_FAILURE(cb, cbd->data);
g_free(cbd);
@@ -293,33 +406,55 @@ static void gobi_pre_sim(struct ofono_modem *modem)
DBG("%p", modem);
- ofono_devinfo_create(modem, 0, "atmodem", data->chat);
- data->sim = ofono_sim_create(modem, 0, "atmodem", data->chat);
+ ofono_devinfo_create(modem, 0, "qmimodem", data->device);
+
+ if (data->features & GOBI_UIM)
+ ofono_sim_create(modem, 0, "qmimodem", data->device);
+ else if (data->features & GOBI_DMS)
+ ofono_sim_create(modem, 0, "qmimodem-legacy", data->device);
}
static void gobi_post_sim(struct ofono_modem *modem)
{
struct gobi_data *data = ofono_modem_get_data(modem);
- struct ofono_gprs *gprs;
- struct ofono_gprs_context *gc;
DBG("%p", modem);
- gprs = ofono_gprs_create(modem, OFONO_VENDOR_GOBI,
- "atmodem", data->chat);
- gc = ofono_gprs_context_create(modem, 0, "atmodem", data->chat);
+ if (data->features & GOBI_CAT)
+ ofono_stk_create(modem, 0, "qmimodem", data->device);
+ else if (data->features & GOBI_CAT_OLD)
+ ofono_stk_create(modem, 1, "qmimodem", data->device);
+
+ if (data->features & GOBI_PBM)
+ ofono_phonebook_create(modem, 0, "qmimodem", data->device);
- if (gprs && gc)
- ofono_gprs_add_context(gprs, gc);
+ if (data->features & GOBI_WMS)
+ ofono_sms_create(modem, 0, "qmimodem", data->device);
+
+ if (data->features & GOBI_PDS)
+ ofono_location_reporting_create(modem, 0, "qmimodem",
+ data->device);
}
static void gobi_post_online(struct ofono_modem *modem)
{
struct gobi_data *data = ofono_modem_get_data(modem);
+ struct ofono_gprs *gprs;
+ struct ofono_gprs_context *gc;
DBG("%p", modem);
- ofono_netreg_create(modem, 0, "dunmodem", data->chat);
+ if (data->features & GOBI_NAS)
+ ofono_netreg_create(modem, 0, "qmimodem", data->device);
+
+ if (data->features & GOBI_WDS) {
+ gprs = ofono_gprs_create(modem, 0, "qmimodem", data->device);
+ gc = ofono_gprs_context_create(modem, 0, "qmimodem",
+ data->device);
+
+ if (gprs && gc)
+ ofono_gprs_add_context(gprs, gc);
+ }
}
static struct ofono_modem_driver gobi_driver = {