diff options
-rw-r--r-- | drivers/atmodem/phonebook.c | 457 |
1 files changed, 208 insertions, 249 deletions
diff --git a/drivers/atmodem/phonebook.c b/drivers/atmodem/phonebook.c index c2fd21bb..d5befc63 100644 --- a/drivers/atmodem/phonebook.c +++ b/drivers/atmodem/phonebook.c @@ -46,23 +46,14 @@ #define CHARSET_SUPPORT (CHARSET_UTF8 | CHARSET_UCS2) static const char *none_prefix[] = { NULL }; -static const char *entries_prefix[] = { "+CPBR:", NULL }; -static const char *charset_prefix[] = { "+CSCS:", NULL }; - -static void at_select_storage(struct ofono_modem *modem, - ofono_generic_cb_t cb, void *data); -static void at_select_charset(struct ofono_modem *modem, - ofono_generic_cb_t cb, void *data); +static const char *cpbr_prefix[] = { "+CPBR:", NULL }; +static const char *cscs_prefix[] = { "+CSCS:", NULL }; +static const char *cpbs_prefix[] = { "+CPBS:", NULL }; struct pb_data { - const char *storage; int index_min, index_max; - int charset_origin; - const char *charset_origin_str; - int charset_current; - int charset_list; - int charset_need_restore; - int has_charset_info; + char *old_charset; + int supported; }; static struct pb_data *phonebook_create() @@ -73,28 +64,51 @@ static struct pb_data *phonebook_create() static void phonebook_destroy(struct pb_data *data) { - g_free((char *)data->charset_origin_str); + if (data->old_charset) + g_free(data->old_charset); g_free(data); } -static const char *ucs2_to_utf8(const char *str) +static char *ucs2_to_utf8(const char *str) { long len; unsigned char *ucs2; - const char *utf8; + char *utf8; ucs2 = decode_hex(str, -1, &len, 0); - utf8 = g_convert(ucs2, len, "UTF-8//TRANSLIT", "UCS-2BE", + utf8 = g_convert((char *)ucs2, len, "UTF-8//TRANSLIT", "UCS-2BE", NULL, NULL, NULL); g_free(ucs2); return utf8; } +static const char *best_charset(int supported) +{ + const char *charset = "Invalid"; + + if (supported & CHARSET_UCS2) + charset = "UCS2"; + + if (supported & CHARSET_UTF8) + charset = "UTF-8"; + + return charset; +} + static void at_cpbr_notify(GAtResult *result, gpointer user_data) { struct cb_data *cbd = user_data; struct ofono_modem *modem = cbd->modem; struct at_data *at = ofono_modem_userdata(modem); GAtResultIter iter; + int current; + + dump_response("at_cbpr_notify", 1, result); + + if (at->pb->supported & CHARSET_UCS2) + current = CHARSET_UCS2; + + if (at->pb->supported & CHARSET_UTF8) + current = CHARSET_UTF8; g_at_result_iter_init(&iter, result); @@ -103,20 +117,14 @@ static void at_cpbr_notify(GAtResult *result, gpointer user_data) const char *number; int type; const char *text; - const char *text_utf8; int hidden = -1; const char *group = NULL; - const char *group_utf8 = NULL; const char *adnumber = NULL; int adtype = -1; const char *secondtext = NULL; - const char *secondtext_utf8 = NULL; const char *email = NULL; - const char *email_utf8 = NULL; const char *sip_uri = NULL; - const char *sip_uri_utf8 = NULL; const char *tel_uri = NULL; - const char *tel_uri_utf8 = NULL; if (!g_at_result_iter_next_number(&iter, &index)) continue; @@ -140,7 +148,14 @@ static void at_cpbr_notify(GAtResult *result, gpointer user_data) g_at_result_iter_next_string(&iter, &tel_uri); /* charset_current is either CHARSET_UCS2 or CHARSET_UTF8 */ - if (at->pb->charset_current & CHARSET_UCS2) { + if (current == CHARSET_UCS2) { + char *text_utf8; + char *group_utf8 = NULL; + char *secondtext_utf8 = NULL; + char *email_utf8 = NULL; + char *sip_uri_utf8 = NULL; + char *tel_uri_utf8 = NULL; + text_utf8 = ucs2_to_utf8(text); if (group) group_utf8 = ucs2_to_utf8(group); @@ -152,31 +167,47 @@ static void at_cpbr_notify(GAtResult *result, gpointer user_data) sip_uri_utf8 = ucs2_to_utf8(sip_uri); if (tel_uri) tel_uri_utf8 = ucs2_to_utf8(tel_uri); - } else { - text_utf8 = text; - group_utf8 = group; - secondtext_utf8 = secondtext; - email_utf8 = email; - sip_uri_utf8 = sip_uri; - tel_uri_utf8 = tel_uri; - } - ofono_phonebook_entry(cbd->modem, index, number, type, + ofono_phonebook_entry(cbd->modem, index, number, type, text_utf8, hidden, group_utf8, adnumber, adtype, secondtext_utf8, email_utf8, sip_uri_utf8, tel_uri_utf8); - if (at->pb->charset_current & CHARSET_UCS2) { - g_free((char *)text_utf8); - g_free((char *)group_utf8); - g_free((char *)secondtext_utf8); - g_free((char *)email_utf8); - g_free((char *)sip_uri_utf8); - g_free((char *)tel_uri_utf8); + g_free(text_utf8); + g_free(group_utf8); + g_free(secondtext_utf8); + g_free(email_utf8); + g_free(sip_uri_utf8); + g_free(tel_uri_utf8); + } else { + ofono_phonebook_entry(cbd->modem, index, number, type, + text, hidden, group, adnumber, + adtype, secondtext, email, + sip_uri, tel_uri); + } } } +static void export_failed(struct cb_data *cbd) +{ + struct ofono_modem *modem = cbd->modem; + struct at_data *at = ofono_modem_userdata(modem); + ofono_generic_cb_t cb = cbd->cb; + + { + DECLARE_FAILURE(error); + cb(&error, cbd->data); + } + + g_free(cbd); + + if (at->pb->old_charset) { + g_free(at->pb->old_charset); + at->pb->old_charset = NULL; + } +} + static void at_read_entries_cb(gboolean ok, GAtResult *result, gpointer user_data) { @@ -184,102 +215,94 @@ static void at_read_entries_cb(gboolean ok, GAtResult *result, struct ofono_modem *modem = cbd->modem; struct at_data *at = ofono_modem_userdata(modem); ofono_generic_cb_t cb = cbd->cb; + const char *charset; + struct ofono_error error; + char buf[32]; - if (at->pb->charset_current != at->pb->charset_origin) - at_select_charset(modem, cb, modem); - else { - struct ofono_error error; - decode_at_error(&error, g_at_result_final_response(result)); - cb(&error, cbd->data); + decode_at_error(&error, g_at_result_final_response(result)); + cb(&error, cbd->data); + g_free(cbd); + + charset = best_charset(at->pb->supported); + + if (strcmp(at->pb->old_charset, charset)) { + sprintf(buf, "AT+CSCS=\"%s\"", at->pb->old_charset); + g_at_chat_send(at->parser, buf, none_prefix, NULL, NULL, NULL); } + + g_free(at->pb->old_charset); + at->pb->old_charset = NULL; } -static void at_read_entries(struct ofono_modem *modem, ofono_generic_cb_t cb, - void *data) +static void at_read_entries(struct cb_data *cbd) { + struct ofono_modem *modem = cbd->modem; struct at_data *at = ofono_modem_userdata(modem); - struct cb_data *cbd = cb_data_new(modem, cb, data); char buf[32]; - if (!cbd) - goto error; - sprintf(buf, "AT+CPBR=%d,%d", at->pb->index_min, at->pb->index_max); - if (g_at_chat_send_listing(at->parser, buf, entries_prefix, + if (g_at_chat_send_listing(at->parser, buf, cpbr_prefix, at_cpbr_notify, at_read_entries_cb, - cbd, g_free) > 0) + cbd, NULL) > 0) return; -error: - if (cbd) - g_free(cbd); - - if (at->pb->charset_origin != at->pb->charset_current) - /* restore the charset */ - at_select_charset(modem, cb, modem); - else { - DECLARE_FAILURE(error); - cb(&error, data); - } + /* If we get here, then most likely connection to the modem dropped + * and we can't really restore the charset anyway + */ + export_failed(cbd); } -static void at_select_charset_cb(gboolean ok, GAtResult *result, - gpointer user_data) +static void at_set_charset_cb(gboolean ok, GAtResult *result, + gpointer user_data) { struct cb_data *cbd = user_data; - struct ofono_modem *modem = cbd->modem; - struct at_data *at = ofono_modem_userdata(modem); - ofono_generic_cb_t cb = cbd->cb; - struct ofono_error error; - if (!ok) - goto out; - - at->pb->charset_need_restore ^= 1; - if (at->pb->charset_need_restore) { - at_read_entries(modem, cb, modem); + if (!ok) { + export_failed(cbd); return; } -out: - decode_at_error(&error, g_at_result_final_response(result)); - cb(&error, cbd->data); - return; + at_read_entries(cbd); } -static void at_select_charset(struct ofono_modem *modem, - ofono_generic_cb_t cb, void *data) +static void at_read_charset_cb(gboolean ok, GAtResult *result, + gpointer user_data) { + struct cb_data *cbd = user_data; + struct ofono_modem *modem = cbd->modem; struct at_data *at = ofono_modem_userdata(modem); - struct cb_data *cbd = cb_data_new(modem, cb, data); - char buf[32]; + GAtResultIter iter; const char *charset; + char buf[32]; - if (!cbd) + dump_response("at_read_charset_cb", ok, result); + + if (!ok) goto error; - if (at->pb->charset_need_restore) - charset = at->pb->charset_origin_str; - else if (at->pb->charset_current == CHARSET_UTF8) - charset = "UTF-8"; - else - charset = "UCS2"; + g_at_result_iter_init(&iter, result); + + if (!g_at_result_iter_next(&iter, "+CSCS:")) + goto error; - sprintf(buf, "AT+CSCS=%s", charset); + g_at_result_iter_next_string(&iter, &charset); + + at->pb->old_charset = g_strdup(charset); + + charset = best_charset(at->pb->supported); + + if (!strcmp(at->pb->old_charset, charset)) { + at_read_entries(cbd); + return; + } + + sprintf(buf, "AT+CSCS=\"%s\"", charset); if (g_at_chat_send(at->parser, buf, none_prefix, - at_select_charset_cb, cbd, g_free) > 0) + at_set_charset_cb, cbd, NULL) > 0) return; error: - if (cbd) - g_free(cbd); - - { - DECLARE_FAILURE(error); - if (at->pb->charset_need_restore) - ofono_error("Phonebook: character can't be restored!"); - cb(&error, data); - } + export_failed(cbd); } static void at_list_indices_cb(gboolean ok, GAtResult *result, @@ -288,7 +311,6 @@ static void at_list_indices_cb(gboolean ok, GAtResult *result, struct cb_data *cbd = user_data; struct ofono_modem *modem = cbd->modem; struct at_data *at = ofono_modem_userdata(modem); - ofono_generic_cb_t cb = cbd->cb; GAtResultIter iter; if (!ok) @@ -311,41 +333,12 @@ static void at_list_indices_cb(gboolean ok, GAtResult *result, if (!g_at_result_iter_close_list(&iter)) goto error; - if (at->pb->charset_origin != at->pb->charset_current) - at_select_charset(modem, cb, modem); - else - at_read_entries(modem, cb, modem); - - return; - -error: - { - DECLARE_FAILURE(e); - cb(&e, cbd->data); - } -} - -static void at_list_indices(struct ofono_modem *modem, - ofono_generic_cb_t cb, void *data) -{ - struct at_data *at = ofono_modem_userdata(modem); - struct cb_data *cbd = cb_data_new(modem, cb, data); - - if (!cbd) - goto error; - - if (g_at_chat_send(at->parser, "AT+CPBR=?", entries_prefix, - at_list_indices_cb, cbd, g_free) > 0) + if (g_at_chat_send(at->parser, "AT+CSCS?", cscs_prefix, + at_read_charset_cb, cbd, NULL) > 0) return; error: - if (cbd) - g_free(cbd); - - { - DECLARE_FAILURE(error); - cb(&error, data); - } + export_failed(cbd); } static void at_select_storage_cb(gboolean ok, GAtResult *result, @@ -353,18 +346,22 @@ static void at_select_storage_cb(gboolean ok, GAtResult *result, { struct cb_data *cbd = user_data; struct ofono_modem *modem = cbd->modem; - ofono_generic_cb_t cb = cbd->cb; + struct at_data *at = ofono_modem_userdata(modem); - if (!ok) { - DECLARE_FAILURE(error); - cb(&error, cbd->data); + dump_response("at_select_storage_cb", ok, result); + + if (!ok) + goto error; + + if (g_at_chat_send(at->parser, "AT+CPBR=?", cpbr_prefix, + at_list_indices_cb, cbd, NULL) > 0) return; - } - at_list_indices(modem, cb, modem); +error: + export_failed(cbd); } -static void at_select_storage(struct ofono_modem *modem, +static void at_export_entries(struct ofono_modem *modem, const char *storage, ofono_generic_cb_t cb, void *data) { struct at_data *at = ofono_modem_userdata(modem); @@ -374,9 +371,9 @@ static void at_select_storage(struct ofono_modem *modem, if (!cbd) goto error; - sprintf(buf, "AT+CPBS=%s", at->pb->storage); + sprintf(buf, "AT+CPBS=\"%s\"", storage); if (g_at_chat_send(at->parser, buf, none_prefix, - at_select_storage_cb, cbd, g_free) > 0) + at_select_storage_cb, cbd, NULL) > 0) return; error: @@ -385,89 +382,80 @@ error: { DECLARE_FAILURE(error); - cb(&error, cbd->data); + cb(&error, data); } } -static void at_read_charset_cb(gboolean ok, GAtResult *result, - gpointer user_data) +static struct ofono_phonebook_ops ops = { + .export_entries = at_export_entries +}; + +static void phonebook_not_supported(struct ofono_modem *modem) { - struct cb_data *cbd = user_data; - struct ofono_modem *modem = cbd->modem; struct at_data *at = ofono_modem_userdata(modem); - ofono_generic_cb_t cb = cbd->cb; + + ofono_error("Phonebook not supported by this modem. If this is in " + "error please submit patches to support this hardware"); + if (at->pb) { + phonebook_destroy(at->pb); + at->pb = NULL; + } +} + +static void at_list_storages_cb(gboolean ok, GAtResult *result, + gpointer user_data) +{ + struct ofono_modem *modem = user_data; + gboolean sm_supported = FALSE; + gboolean me_supported = FALSE; + gboolean in_list = FALSE; GAtResultIter iter; - const char *charset; + const char *storage; + + dump_response("at_list_storages_cb", ok, result); if (!ok) goto error; g_at_result_iter_init(&iter, result); - - if (!g_at_result_iter_next(&iter, "+CSCS:")) + if (!g_at_result_iter_next(&iter, "+CPBS:")) goto error; - g_at_result_iter_next_string(&iter, &charset); - at->pb->charset_origin_str = g_strdup(charset); - if (!strcmp(charset, "UCS2")) - at->pb->charset_origin = CHARSET_UCS2; - else if (!strcmp(charset, "UTF-8")) - at->pb->charset_origin = CHARSET_UTF8; - - if (at->pb->charset_origin & CHARSET_SUPPORT) - at->pb->charset_current = at->pb->charset_origin; - else if (at->pb->charset_list & CHARSET_UTF8) - at->pb->charset_current = CHARSET_UTF8; - else - at->pb->charset_current = CHARSET_UCS2; - - at_select_storage(modem, cb, modem); - return; + /* Some modems don't report CPBS in a proper list */ + if (g_at_result_iter_open_list(&iter)) + in_list = TRUE; -error: - ofono_error("Phonebook: at_read_charset_cb failed"); - { - DECLARE_FAILURE(error); - cb(&error, cbd->data); + while (g_at_result_iter_next_string(&iter, &storage)) { + if (!strcmp(storage, "ME")) + me_supported = TRUE; + else if (!strcmp(storage, "SM")) + sm_supported = TRUE; } -} - -static void at_read_charset(struct ofono_modem *modem, - ofono_generic_cb_t cb, void *data) -{ - struct at_data *at = ofono_modem_userdata(modem); - struct cb_data *cbd = cb_data_new(modem, cb, data); - if (g_at_chat_send(at->parser, "AT+CSCS?", charset_prefix, - at_read_charset_cb, cbd, g_free) > 0) - return; + if (in_list && !g_at_result_iter_close_list(&iter)) + goto error; - { - DECLARE_FAILURE(error); - cb(&error, data); - } -} + if (!me_supported && !sm_supported) + goto error; -static gboolean is_valid_charset_list(struct pb_data *pb) -{ - if (!(pb->charset_list & CHARSET_SUPPORT)) { - ofono_error("Phonebook: not a valid charset_list"); - return FALSE; - } + ofono_phonebook_register(modem, &ops); + return; - return TRUE; +error: + phonebook_not_supported(modem); } static void at_list_charsets_cb(gboolean ok, GAtResult *result, gpointer user_data) { - struct cb_data *cbd = user_data; - struct ofono_modem *modem = cbd->modem; + struct ofono_modem *modem = user_data; struct at_data *at = ofono_modem_userdata(modem); - ofono_generic_cb_t cb = cbd->cb; + gboolean in_list = FALSE; GAtResultIter iter; const char *charset; + dump_response("at_list_charsets_cb", ok, result); + if (!ok) goto error; @@ -475,77 +463,48 @@ static void at_list_charsets_cb(gboolean ok, GAtResult *result, if (!g_at_result_iter_next(&iter, "+CSCS:")) goto error; + /* Some modems don't report CPBS in a proper list */ + if (g_at_result_iter_open_list(&iter)) + in_list = TRUE; + while (g_at_result_iter_next_string(&iter, &charset)) { if (!strcmp(charset, "UTF-8")) - at->pb->charset_list |= CHARSET_UTF8; + at->pb->supported |= CHARSET_UTF8; else if (!strcmp(charset, "UCS2")) - at->pb->charset_list |= CHARSET_UCS2; + at->pb->supported |= CHARSET_UCS2; } - if (!is_valid_charset_list(at->pb)) + if (in_list && g_at_result_iter_close_list(&iter)) goto error; - at_read_charset(modem, cb, modem); - return; - -error: - ofono_error("Phonebook: at_list_charsets_cb failed"); - { - DECLARE_FAILURE(error); - cb(&error, cbd->data); - } -} - -static void at_list_charsets(struct ofono_modem *modem, - ofono_generic_cb_t cb, void *data) -{ - struct at_data *at = ofono_modem_userdata(modem); - struct cb_data *cbd = cb_data_new(modem, cb, data); + if (!(at->pb->supported & CHARSET_SUPPORT)) + goto error; - if (g_at_chat_send(at->parser, "AT+CSCS=?", charset_prefix, - at_list_charsets_cb, cbd, g_free) > 0) + if (g_at_chat_send(at->parser, "AT+CPBS=?", cpbs_prefix, + at_list_storages_cb, modem, NULL) > 0) return; - { - DECLARE_FAILURE(error); - cb(&error, data); - } +error: + phonebook_not_supported(modem); } -static void at_export_entries(struct ofono_modem *modem, const char *storage, - ofono_generic_cb_t cb, void *data) +static void at_list_charsets(struct ofono_modem *modem) { struct at_data *at = ofono_modem_userdata(modem); - at->pb->storage = storage; - - if (at->pb->has_charset_info) { - if (!is_valid_charset_list(at->pb)) - goto error; - at_select_storage(modem, cb, modem); - } else { - at->pb->has_charset_info = 1; - at_list_charsets(modem, cb, data); - } - return; + if (g_at_chat_send(at->parser, "AT+CSCS=?", cscs_prefix, + at_list_charsets_cb, modem, NULL) > 0) + return; -error: - { - DECLARE_FAILURE(error); - cb(&error, data); - } + phonebook_not_supported(modem); } -static struct ofono_phonebook_ops ops = { - .export_entries = at_export_entries -}; - void at_phonebook_init(struct ofono_modem *modem) { struct at_data *at = ofono_modem_userdata(modem); - ofono_phonebook_register(modem, &ops); at->pb = phonebook_create(); + at_list_charsets(modem); } void at_phonebook_exit(struct ofono_modem *modem) |