3 * oFono - Open Source Telephony
5 * Copyright (C) 2008-2011 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
34 #define OFONO_API_SUBJECT_TO_CHANGE
35 #include <ofono/plugin.h>
36 #include <ofono/modem.h>
37 #include <ofono/devinfo.h>
38 #include <ofono/netreg.h>
39 #include <ofono/sim.h>
40 #include <ofono/cbs.h>
41 #include <ofono/sms.h>
42 #include <ofono/ussd.h>
43 #include <ofono/gprs.h>
44 #include <ofono/gprs-context.h>
45 #include <ofono/audio-settings.h>
46 #include <ofono/radio-settings.h>
47 #include <ofono/voicecall.h>
48 #include <ofono/call-forwarding.h>
49 #include <ofono/call-settings.h>
50 #include <ofono/call-barring.h>
51 #include <ofono/phonebook.h>
52 #include <ofono/message-waiting.h>
53 #include <ofono/cdma-netreg.h>
54 #include <ofono/cdma-connman.h>
55 #include <ofono/log.h>
57 #include <drivers/atmodem/atutil.h>
58 #include <drivers/atmodem/vendor.h>
60 static const char *none_prefix[] = { NULL };
61 static const char *gcap_prefix[] = { "+GCAP:", NULL };
62 static const char *rfswitch_prefix[] = { "^RFSWITCH:", NULL };
63 static const char *sysinfo_prefix[] = { "^SYSINFO:", NULL };
64 static const char *ussdmode_prefix[] = { "^USSDMODE:", NULL };
65 static const char *dialmode_prefix[] = { "^DIALMODE:", NULL };
66 static const char *cvoice_prefix[] = { "^CVOICE:", NULL };
69 SIM_STATE_INVALID_OR_LOCKED = 0,
71 SIM_STATE_INVALID_CS = 2,
72 SIM_STATE_INVALID_PS = 3,
73 SIM_STATE_INVALID_PS_AND_CS = 4,
74 SIM_STATE_ROMSIM = 240,
75 SIM_STATE_NOT_EXISTENT = 255,
83 guint sysinfo_poll_source;
84 guint sysinfo_poll_count;
85 struct cb_data *online_cbd;
86 const char *offline_command;
92 static int huawei_probe(struct ofono_modem *modem)
94 struct huawei_data *data;
98 data = g_try_new0(struct huawei_data, 1);
102 ofono_modem_set_data(modem, data);
107 static void huawei_remove(struct ofono_modem *modem)
109 struct huawei_data *data = ofono_modem_get_data(modem);
113 ofono_modem_set_data(modem, NULL);
115 /* Cleanup after potential enable polling */
116 if (data->sysinfo_poll_source > 0)
117 g_source_remove(data->sysinfo_poll_source);
119 /* Cleanup after hot-unplug */
120 g_at_chat_unref(data->pcui);
125 static void huawei_debug(const char *str, void *user_data)
127 const char *prefix = user_data;
129 ofono_info("%s%s", prefix, str);
132 static void ussdmode_query_cb(gboolean ok, GAtResult *result,
135 struct huawei_data *data = user_data;
142 g_at_result_iter_init(&iter, result);
144 if (!g_at_result_iter_next(&iter, "^USSDMODE:"))
147 if (!g_at_result_iter_next_number(&iter, &ussdmode))
153 /* Set USSD mode to text mode */
154 g_at_chat_send(data->pcui, "AT^USSDMODE=0", none_prefix,
158 static void ussdmode_support_cb(gboolean ok, GAtResult *result,
161 struct huawei_data *data = user_data;
167 g_at_result_iter_init(&iter, result);
169 if (!g_at_result_iter_next(&iter, "^USSDMODE:"))
172 /* Query current USSD mode */
173 g_at_chat_send(data->pcui, "AT^USSDMODE?", ussdmode_prefix,
174 ussdmode_query_cb, data, NULL);
177 static void dialmode_query_cb(gboolean ok, GAtResult *result,
180 //struct huawei_data *data = user_data;
186 g_at_result_iter_init(&iter, result);
188 if (!g_at_result_iter_next(&iter, "^DIALMODE:"))
192 static void dialmode_support_cb(gboolean ok, GAtResult *result,
195 struct huawei_data *data = user_data;
201 g_at_result_iter_init(&iter, result);
203 if (!g_at_result_iter_next(&iter, "^DIALMODE:"))
206 /* Query current NDIS mode */
207 g_at_chat_send(data->pcui, "AT^DIALMODE?", dialmode_prefix,
208 dialmode_query_cb, data, NULL);
211 static void cvoice_query_cb(gboolean ok, GAtResult *result,
214 struct ofono_modem *modem = user_data;
215 struct huawei_data *data = ofono_modem_get_data(modem);
217 gint mode, rate, bits, period;
222 g_at_result_iter_init(&iter, result);
224 if (!g_at_result_iter_next(&iter, "^CVOICE:"))
227 if (!g_at_result_iter_next_number(&iter, &mode))
230 if (!g_at_result_iter_next_number(&iter, &rate))
233 if (!g_at_result_iter_next_number(&iter, &bits))
236 if (!g_at_result_iter_next_number(&iter, &period))
239 data->have_voice = TRUE;
241 ofono_info("Voice channel: %d Hz, %d bits, %dms period",
244 /* Check available voice ports */
245 g_at_chat_send(data->pcui, "AT^DDSETEX=?", none_prefix,
249 static void cvoice_support_cb(gboolean ok, GAtResult *result,
252 struct ofono_modem *modem = user_data;
253 struct huawei_data *data = ofono_modem_get_data(modem);
259 g_at_result_iter_init(&iter, result);
261 if (!g_at_result_iter_next(&iter, "^CVOICE:"))
264 /* Query current voice setting */
265 g_at_chat_send(data->pcui, "AT^CVOICE?", cvoice_prefix,
266 cvoice_query_cb, modem, NULL);
269 static void simst_notify(GAtResult *result, gpointer user_data)
271 struct ofono_modem *modem = user_data;
272 struct huawei_data *data = ofono_modem_get_data(modem);
276 g_at_result_iter_init(&iter, result);
278 if (!g_at_result_iter_next(&iter, "^SIMST:"))
281 if (!g_at_result_iter_next_number(&iter, &sim_state))
284 DBG("%d -> %d", data->sim_state, sim_state);
286 data->sim_state = sim_state;
289 static gboolean parse_sysinfo_result(GAtResult *result, int *srv_status,
290 int *srv_domain, int *sim_state)
294 g_at_result_iter_init(&iter, result);
296 if (!g_at_result_iter_next(&iter, "^SYSINFO:"))
299 if (!g_at_result_iter_next_number(&iter, srv_status))
302 if (!g_at_result_iter_next_number(&iter, srv_domain))
305 if (!g_at_result_iter_skip_next(&iter))
308 if (!g_at_result_iter_skip_next(&iter))
311 if (!g_at_result_iter_next_number(&iter, sim_state))
317 static void shutdown_device(struct huawei_data *data)
319 g_at_chat_cancel_all(data->modem);
320 g_at_chat_unregister_all(data->modem);
322 g_at_chat_unref(data->modem);
325 g_at_chat_cancel_all(data->pcui);
326 g_at_chat_unregister_all(data->pcui);
328 g_at_chat_unref(data->pcui);
332 static void cfun_offline(gboolean ok, GAtResult *result, gpointer user_data)
334 struct ofono_modem *modem = user_data;
335 struct huawei_data *data = ofono_modem_get_data(modem);
340 shutdown_device(data);
341 ofono_modem_set_powered(modem, FALSE);
345 ofono_modem_set_powered(modem, TRUE);
348 static gboolean sysinfo_enable_check(gpointer user_data);
350 static void sysinfo_enable_cb(gboolean ok, GAtResult *result,
353 struct ofono_modem *modem = user_data;
354 struct huawei_data *data = ofono_modem_get_data(modem);
355 int srv_status, srv_domain, sim_state;
360 if (parse_sysinfo_result(result, &srv_status, &srv_domain,
361 &sim_state) == FALSE)
364 DBG("%d -> %d", data->sim_state, sim_state);
366 data->sim_state = sim_state;
368 if (sim_state == SIM_STATE_NOT_EXISTENT) {
369 data->sysinfo_poll_count++;
371 if (data->sysinfo_poll_count > 5)
374 data->sysinfo_poll_source = g_timeout_add_seconds(1,
375 sysinfo_enable_check, modem);
379 data->have_sim = TRUE;
381 /* Switch data carrier detect signal off */
382 g_at_chat_send(data->modem, "AT&C0", NULL, NULL, NULL, NULL);
383 g_at_chat_send(data->pcui, "AT&C0", NULL, NULL, NULL, NULL);
385 /* Query current device settings */
386 g_at_chat_send(data->pcui, "AT^U2DIAG?", none_prefix,
389 /* Query current port settings */
390 g_at_chat_send(data->pcui, "AT^GETPORTMODE", none_prefix,
393 /* Check USSD mode support */
394 g_at_chat_send(data->pcui, "AT^USSDMODE=?", ussdmode_prefix,
395 ussdmode_support_cb, data, NULL);
397 /* Check NDIS mode support */
398 g_at_chat_send(data->pcui, "AT^DIALMODE=?", dialmode_prefix,
399 dialmode_support_cb, data, NULL);
401 /* Check for voice support */
402 g_at_chat_send(data->pcui, "AT^CVOICE=?", cvoice_prefix,
403 cvoice_support_cb, modem, NULL);
405 if (g_at_chat_send(data->pcui, data->offline_command, none_prefix,
406 cfun_offline, modem, NULL) > 0)
410 shutdown_device(data);
411 ofono_modem_set_powered(modem, FALSE);
414 static gboolean sysinfo_enable_check(gpointer user_data)
416 struct ofono_modem *modem = user_data;
417 struct huawei_data *data = ofono_modem_get_data(modem);
419 data->sysinfo_poll_source = 0;
421 g_at_chat_send(data->pcui, "AT^SYSINFO", sysinfo_prefix,
422 sysinfo_enable_cb, modem, NULL);
427 static void cfun_enable(gboolean ok, GAtResult *result, gpointer user_data)
429 struct ofono_modem *modem = user_data;
430 struct huawei_data *data = ofono_modem_get_data(modem);
435 shutdown_device(data);
436 ofono_modem_set_powered(modem, FALSE);
440 /* Follow sim state changes */
441 g_at_chat_register(data->pcui, "^SIMST:", simst_notify,
444 data->sysinfo_poll_count = 0;
446 sysinfo_enable_check(modem);
449 static void rfswitch_support(gboolean ok, GAtResult *result, gpointer user_data)
451 struct ofono_modem *modem = user_data;
452 struct huawei_data *data = ofono_modem_get_data(modem);
454 if (data->have_gsm == FALSE && data->have_cdma == TRUE) {
455 data->offline_command = "AT+CFUN=5";
460 data->offline_command = "AT+CFUN=5";
462 data->offline_command = "AT+CFUN=7";
465 g_at_chat_send(data->pcui, "AT+CFUN=1", none_prefix,
466 cfun_enable, modem, NULL);
469 static void gcap_support(gboolean ok, GAtResult *result, gpointer user_data)
471 struct ofono_modem *modem = user_data;
472 struct huawei_data *data = ofono_modem_get_data(modem);
479 g_at_result_iter_init(&iter, result);
481 if (!g_at_result_iter_next(&iter, "+GCAP:"))
484 while (g_at_result_iter_next_unquoted_string(&iter, &gcap)) {
488 if (!strcmp(gcap, "+CGSM"))
489 data->have_gsm = TRUE;
490 else if (!strcmp(gcap, "+CIS707-A"))
491 data->have_cdma = TRUE;
495 g_at_chat_send(data->pcui, "AT^RFSWITCH=?", rfswitch_prefix,
496 rfswitch_support, modem, NULL);
499 static GAtChat *open_device(struct ofono_modem *modem,
500 const char *key, char *debug)
507 device = ofono_modem_get_string(modem, key);
511 DBG("%s %s", key, device);
513 channel = g_at_tty_open(device, NULL);
517 syntax = g_at_syntax_new_gsm_permissive();
518 chat = g_at_chat_new(channel, syntax);
519 g_at_syntax_unref(syntax);
521 g_io_channel_unref(channel);
526 g_at_chat_add_terminator(chat, "COMMAND NOT SUPPORT", -1, FALSE);
527 g_at_chat_add_terminator(chat, "TOO MANY PARAMETERS", -1, FALSE);
529 if (getenv("OFONO_AT_DEBUG"))
530 g_at_chat_set_debug(chat, huawei_debug, debug);
535 static int huawei_enable(struct ofono_modem *modem)
537 struct huawei_data *data = ofono_modem_get_data(modem);
541 data->modem = open_device(modem, "Modem", "Modem: ");
542 if (data->modem == NULL)
545 data->pcui = open_device(modem, "Pcui", "PCUI: ");
546 if (data->pcui == NULL) {
547 g_at_chat_unref(data->modem);
552 g_at_chat_set_slave(data->modem, data->pcui);
554 g_at_chat_send(data->modem, "ATE0 +CMEE=1", NULL, NULL, NULL, NULL);
555 g_at_chat_send(data->pcui, "ATE0 +CMEE=1", NULL, NULL, NULL, NULL);
558 * Ensure that the modem is using GSM character set and not IRA,
559 * otherwise weirdness with umlauts and other non-ASCII characters
562 g_at_chat_send(data->modem, "AT+CSCS=\"GSM\"", none_prefix,
564 g_at_chat_send(data->pcui, "AT+CSCS=\"GSM\"", none_prefix,
567 data->sim_state = SIM_STATE_NOT_EXISTENT;
569 /* Check for GSM capabilities */
570 g_at_chat_send(data->pcui, "ATI", gcap_prefix,
571 gcap_support, modem, NULL);
576 static void cfun_disable(gboolean ok, GAtResult *result, gpointer user_data)
578 struct ofono_modem *modem = user_data;
579 struct huawei_data *data = ofono_modem_get_data(modem);
583 g_at_chat_unref(data->pcui);
587 ofono_modem_set_powered(modem, FALSE);
590 static int huawei_disable(struct ofono_modem *modem)
592 struct huawei_data *data = ofono_modem_get_data(modem);
596 g_at_chat_cancel_all(data->modem);
597 g_at_chat_unregister_all(data->modem);
599 g_at_chat_unref(data->modem);
602 g_at_chat_cancel_all(data->pcui);
603 g_at_chat_unregister_all(data->pcui);
605 /* Cleanup potential online enable polling */
606 if (data->sysinfo_poll_source > 0) {
607 g_source_remove(data->sysinfo_poll_source);
608 data->sysinfo_poll_source = 0;
610 g_free(data->online_cbd);
611 data->online_cbd = NULL;
614 g_at_chat_send(data->pcui, "AT+CFUN=0", none_prefix,
615 cfun_disable, modem, NULL);
620 static gboolean sysinfo_online_check(gpointer user_data);
622 static void sysinfo_online_cb(gboolean ok, GAtResult *result,
625 struct huawei_data *data = user_data;
626 ofono_modem_online_cb_t cb = data->online_cbd->cb;
627 int srv_status, srv_domain, sim_state;
632 if (parse_sysinfo_result(result, &srv_status, &srv_domain,
633 &sim_state) == FALSE)
636 DBG("%d -> %d", data->sim_state, sim_state);
638 data->sim_state = sim_state;
640 /* Valid service status and at minimum PS domain */
641 if (srv_status > 0 && srv_domain > 1) {
642 CALLBACK_WITH_SUCCESS(cb, data->online_cbd->data);
647 case SIM_STATE_VALID:
648 case SIM_STATE_INVALID_CS:
649 case SIM_STATE_INVALID_PS:
650 case SIM_STATE_INVALID_PS_AND_CS:
651 case SIM_STATE_ROMSIM:
652 CALLBACK_WITH_SUCCESS(cb, data->online_cbd->data);
656 data->sysinfo_poll_count++;
658 if (data->sysinfo_poll_count > 15)
661 data->sysinfo_poll_source = g_timeout_add_seconds(2,
662 sysinfo_online_check, data);
666 CALLBACK_WITH_FAILURE(cb, data->online_cbd->data);
669 g_free(data->online_cbd);
670 data->online_cbd = NULL;
673 static gboolean sysinfo_online_check(gpointer user_data)
675 struct huawei_data *data = user_data;
677 data->sysinfo_poll_source = 0;
679 g_at_chat_send(data->pcui, "AT^SYSINFO", sysinfo_prefix,
680 sysinfo_online_cb, data, NULL);
685 static void set_online_cb(gboolean ok, GAtResult *result, gpointer user_data)
687 struct ofono_modem *modem = user_data;
688 struct huawei_data *data = ofono_modem_get_data(modem);
691 ofono_modem_online_cb_t cb = data->online_cbd->cb;
693 CALLBACK_WITH_FAILURE(cb, data->online_cbd->data);
695 g_free(data->online_cbd);
696 data->online_cbd = NULL;
700 data->sysinfo_poll_count = 0;
702 sysinfo_online_check(data);
705 static void set_offline_cb(gboolean ok, GAtResult *result, gpointer user_data)
707 struct cb_data *cbd = user_data;
708 ofono_modem_online_cb_t cb = cbd->cb;
709 struct ofono_error error;
711 decode_at_error(&error, g_at_result_final_response(result));
712 cb(&error, cbd->data);
715 static void huawei_set_online(struct ofono_modem *modem, ofono_bool_t online,
716 ofono_modem_online_cb_t cb, void *user_data)
718 struct huawei_data *data = ofono_modem_get_data(modem);
720 DBG("modem %p %s", modem, online ? "online" : "offline");
722 if (online == TRUE) {
723 data->online_cbd = cb_data_new(cb, user_data);
725 if (g_at_chat_send(data->pcui, "AT+CFUN=1", none_prefix,
726 set_online_cb, modem, NULL) > 0)
729 g_free(data->online_cbd);
730 data->online_cbd = NULL;
732 struct cb_data *cbd = cb_data_new(cb, user_data);
734 if (g_at_chat_send(data->pcui, data->offline_command,
735 none_prefix, set_offline_cb, cbd, g_free) > 0)
741 CALLBACK_WITH_FAILURE(cb, user_data);
744 static void huawei_pre_sim(struct ofono_modem *modem)
746 struct huawei_data *data = ofono_modem_get_data(modem);
750 if (data->have_gsm == TRUE) {
751 struct ofono_sim *sim;
753 ofono_devinfo_create(modem, 0, "atmodem", data->pcui);
754 sim = ofono_sim_create(modem, OFONO_VENDOR_HUAWEI,
755 "atmodem", data->pcui);
757 if (sim && data->have_sim == TRUE)
758 ofono_sim_inserted_notify(sim, TRUE);
759 } else if (data->have_cdma == TRUE) {
760 ofono_devinfo_create(modem, 0, "cdmamodem", data->pcui);
764 static void huawei_post_sim(struct ofono_modem *modem)
766 struct huawei_data *data = ofono_modem_get_data(modem);
770 if (data->have_voice == TRUE) {
771 ofono_voicecall_create(modem, 0, "huaweimodem", data->pcui);
772 ofono_audio_settings_create(modem, 0,
773 "huaweimodem", data->pcui);
776 if (data->have_gsm == TRUE) {
777 struct ofono_gprs *gprs;
778 struct ofono_gprs_context *gc;
780 ofono_phonebook_create(modem, 0, "atmodem", data->pcui);
781 ofono_radio_settings_create(modem, 0,
782 "huaweimodem", data->pcui);
784 ofono_sms_create(modem, OFONO_VENDOR_HUAWEI,
785 "atmodem", data->pcui);
787 gprs = ofono_gprs_create(modem, OFONO_VENDOR_HUAWEI,
788 "atmodem", data->pcui);
789 gc = ofono_gprs_context_create(modem, 0,
790 "atmodem", data->modem);
793 ofono_gprs_add_context(gprs, gc);
797 static void huawei_post_online(struct ofono_modem *modem)
799 struct huawei_data *data = ofono_modem_get_data(modem);
803 if (data->have_gsm == TRUE) {
804 ofono_netreg_create(modem, OFONO_VENDOR_HUAWEI,
805 "atmodem", data->pcui);
807 ofono_cbs_create(modem, OFONO_VENDOR_QUALCOMM_MSM,
808 "atmodem", data->pcui);
809 ofono_ussd_create(modem, OFONO_VENDOR_QUALCOMM_MSM,
810 "atmodem", data->pcui);
811 } else if (data->have_cdma == TRUE) {
812 ofono_cdma_netreg_create(modem, 0, "huaweimodem", data->pcui);
814 ofono_cdma_connman_create(modem, OFONO_VENDOR_HUAWEI,
815 "cdmamodem", data->modem);
818 if (data->have_voice == TRUE) {
819 struct ofono_message_waiting *mw;
821 ofono_call_forwarding_create(modem, 0, "atmodem", data->pcui);
822 ofono_call_settings_create(modem, 0, "atmodem", data->pcui);
823 ofono_call_barring_create(modem, 0, "atmodem", data->pcui);
825 mw = ofono_message_waiting_create(modem);
827 ofono_message_waiting_register(mw);
831 static struct ofono_modem_driver huawei_driver = {
833 .probe = huawei_probe,
834 .remove = huawei_remove,
835 .enable = huawei_enable,
836 .disable = huawei_disable,
837 .set_online = huawei_set_online,
838 .pre_sim = huawei_pre_sim,
839 .post_sim = huawei_post_sim,
840 .post_online = huawei_post_online,
843 static int huawei_init(void)
845 return ofono_modem_driver_register(&huawei_driver);
848 static void huawei_exit(void)
850 ofono_modem_driver_unregister(&huawei_driver);
853 OFONO_PLUGIN_DEFINE(huawei, "HUAWEI Mobile modem driver", VERSION,
854 OFONO_PLUGIN_PRIORITY_DEFAULT, huawei_init, huawei_exit)