diff --git a/drivers/atmodem/gprs.c b/drivers/atmodem/gprs.c index 10108281..16f4927f 100644 --- a/drivers/atmodem/gprs.c +++ b/drivers/atmodem/gprs.c @@ -29,6 +29,7 @@ #include #include +#include #include #include @@ -41,6 +42,8 @@ #include "atmodem.h" #include "vendor.h" +#define MAX_CONTEXTS 255 + static const char *cgreg_prefix[] = { "+CGREG:", NULL }; static const char *cgdcont_prefix[] = { "+CGDCONT:", NULL }; static const char *cgact_prefix[] = { "+CGACT:", NULL }; @@ -54,6 +57,47 @@ struct gprs_data { int attached; }; +struct list_contexts_data +{ + struct ofono_gprs *gprs; + void *cb; + void *data; + struct l_uintset *active_cids; + int ref_count; +}; + +static struct list_contexts_data * list_contexts_data_new( + struct ofono_gprs *gprs, void *cb, void *data) +{ + struct list_contexts_data *ret; + + ret = g_new0(struct list_contexts_data, 1); + ret->ref_count = 1; + ret->gprs = gprs; + ret->cb = cb; + ret->data = data; + + return ret; +} + +static struct list_contexts_data * list_contexts_data_ref( + struct list_contexts_data *ld) +{ + ld->ref_count++; + return ld; +} + +static void list_contexts_data_unref(gpointer user_data) +{ + struct list_contexts_data *ld = user_data; + + if (--ld->ref_count) + return; + + l_uintset_free(ld->active_cids); + g_free(ld); +} + static void at_cgatt_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct cb_data *cbd = user_data; @@ -146,14 +190,43 @@ static void at_gprs_registration_status(struct ofono_gprs *gprs, CALLBACK_WITH_FAILURE(cb, -1, data); } +static void at_cgdcont_parse(struct ofono_gprs *gprs, GAtResult *result, + struct l_uintset *cids) +{ + GAtResultIter iter; + + g_at_result_iter_init(&iter, result); + + while (g_at_result_iter_next(&iter, "+CGDCONT:")) { + int read_cid; + const char *apn = NULL; + + if (!g_at_result_iter_next_number(&iter, &read_cid)) + break; + + if (!l_uintset_contains(cids, read_cid)) + continue; + + /* ignore protocol */ + g_at_result_iter_skip_next(&iter); + + g_at_result_iter_next_string(&iter, &apn); + + if (apn) + ofono_gprs_cid_activated(gprs, read_cid, apn); + else + ofono_warn("cid %d: Activated but no apn present", + read_cid); + } +} + static void at_cgdcont_read_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct ofono_gprs *gprs = user_data; struct gprs_data *gd = ofono_gprs_get_data(gprs); int activated_cid = gd->last_auto_context_id; - const char *apn = NULL; - GAtResultIter iter; + struct l_uintset *cids; DBG("ok %d", ok); @@ -162,47 +235,52 @@ static void at_cgdcont_read_cb(gboolean ok, GAtResult *result, return; } - if (gd->last_auto_context_id == -1) { + if (activated_cid == -1) { DBG("Context got deactivated while calling CGDCONT"); return; } - g_at_result_iter_init(&iter, result); + cids = l_uintset_new(activated_cid); - while (g_at_result_iter_next(&iter, "+CGDCONT:")) { - int read_cid; + l_uintset_put(cids, activated_cid); - if (!g_at_result_iter_next_number(&iter, &read_cid)) - break; + at_cgdcont_parse(gprs, result, cids); - if (read_cid != activated_cid) - continue; + l_uintset_free(cids); +} - /* ignore protocol */ - g_at_result_iter_skip_next(&iter); +static void at_cgdcont_act_read_cb(gboolean ok, GAtResult *result, + gpointer user_data) +{ + struct list_contexts_data *ld = user_data; + ofono_gprs_cb_t cb = ld->cb; + struct ofono_error error; - g_at_result_iter_next_string(&iter, &apn); + decode_at_error(&error, g_at_result_final_response(result)); - break; - } - - if (apn) - ofono_gprs_cid_activated(gprs, activated_cid, apn); + if (!ok) + ofono_warn("Can't read CGDCONT context."); else - ofono_warn("cid %u: Received activated but no apn present", - activated_cid); + at_cgdcont_parse(ld->gprs, result, ld->active_cids); + + cb(&error, ld->data); } static void at_cgact_cb(gboolean ok, GAtResult *result, gpointer user_data) { - struct ofono_gprs *gprs = user_data; - struct gprs_data *gd = ofono_gprs_get_data(gprs); + struct list_contexts_data *ld = user_data; + struct gprs_data *gd = ofono_gprs_get_data(ld->gprs); + ofono_gprs_cb_t cb = ld->cb; + struct ofono_error error; GAtResultIter iter; - DBG("ok %d", ok); + decode_at_error(&error, g_at_result_final_response(result)); if (!ok) { ofono_warn("Can't read CGACT contexts."); + + cb(&error, ld->data); + return; } @@ -222,17 +300,43 @@ static void at_cgact_cb(gboolean ok, GAtResult *result, gpointer user_data) continue; /* Flag this as auto context as it was obviously active */ - if (gd->last_auto_context_id == 0) + if (gd->last_auto_context_id == -1) gd->last_auto_context_id = read_cid; - if (read_cid != gd->last_auto_context_id) - continue; + if (!ld->active_cids) + ld->active_cids = l_uintset_new(MAX_CONTEXTS); - g_at_chat_send(gd->chat, "AT+CGDCONT?", cgdcont_prefix, - at_cgdcont_read_cb, gprs, NULL); - - break; + l_uintset_put(ld->active_cids, read_cid); } + + if (ld->active_cids != NULL) { + if (g_at_chat_send(gd->chat, "AT+CGDCONT?", cgdcont_prefix, + at_cgdcont_act_read_cb, ld, + list_contexts_data_unref)) { + list_contexts_data_ref(ld); + return; + } + + CALLBACK_WITH_FAILURE(cb, ld->data); + } else { + /* No active contexts found */ + cb(&error, ld->data); + } +} + +static void at_gprs_list_active_contexts(struct ofono_gprs *gprs, + ofono_gprs_cb_t cb, void *data) +{ + struct gprs_data *gd = ofono_gprs_get_data(gprs); + struct list_contexts_data *ld = list_contexts_data_new(gprs, cb, data); + + if (g_at_chat_send(gd->chat, "AT+CGACT?", cgact_prefix, + at_cgact_cb, ld, list_contexts_data_unref)) + return; + + list_contexts_data_unref(ld); + + CALLBACK_WITH_FAILURE(cb, data); } static void cgreg_notify(GAtResult *result, gpointer user_data) @@ -538,10 +642,6 @@ static void gprs_initialized(gboolean ok, GAtResult *result, gpointer user_data) break; } - /* Check if there is any already activated contexts at init */ - g_at_chat_send(gd->chat, "AT+CGACT?", cgact_prefix, - at_cgact_cb, gprs, NULL); - ofono_gprs_register(gprs); } @@ -705,6 +805,7 @@ static const struct ofono_gprs_driver driver = { .remove = at_gprs_remove, .set_attached = at_gprs_set_attached, .attached_status = at_gprs_registration_status, + .list_active_contexts = at_gprs_list_active_contexts, }; void at_gprs_init(void)