diff options
Diffstat (limited to 'plugins')
40 files changed, 4254 insertions, 1406 deletions
diff --git a/plugins/77-mm-ericsson-mbm.rules b/plugins/77-mm-ericsson-mbm.rules index 71dc6b8..8804036 100644 --- a/plugins/77-mm-ericsson-mbm.rules +++ b/plugins/77-mm-ericsson-mbm.rules @@ -20,9 +20,15 @@ ATTRS{idVendor}=="0bdb", ATTRS{idProduct}=="1909", ENV{ID_MM_ERICSSON_MBM}="1" # Ericsson C3607w ATTRS{idVendor}=="0bdb", ATTRS{idProduct}=="1049", ENV{ID_MM_ERICSSON_MBM}="1" +# Ericsson C3607w v2 +ATTRS{idVendor}=="0bdb", ATTRS{idProduct}=="190b", ENV{ID_MM_ERICSSON_MBM}="1" + # Sony-Ericsson MD300 ATTRS{idVendor}=="0fce", ATTRS{idProduct}=="d0cf", ENV{ID_MM_ERICSSON_MBM}="1" +# Sony-Ericsson MD400 +ATTRS{idVendor}=="0fce", ATTRS{idProduct}=="d0e1", ENV{ID_MM_ERICSSON_MBM}="1" + # Dell 5530 HSDPA ATTRS{idVendor}=="413c", ATTRS{idProduct}=="8147", ENV{ID_MM_ERICSSON_MBM}="1" @@ -30,6 +36,10 @@ ATTRS{idVendor}=="413c", ATTRS{idProduct}=="8147", ENV{ID_MM_ERICSSON_MBM}="1" ATTRS{idVendor}=="413c", ATTRS{idProduct}=="8183", ENV{ID_MM_ERICSSON_MBM}="1" ATTRS{idVendor}=="413c", ATTRS{idProduct}=="8184", ENV{ID_MM_ERICSSON_MBM}="1" +# Dell F3307 +ATTRS{idVendor}=="413c", ATTRS{idProduct}=="818b", ENV{ID_MM_ERICSSON_MBM}="1" +ATTRS{idVendor}=="413c", ATTRS{idProduct}=="818c", ENV{ID_MM_ERICSSON_MBM}="1" + # Toshiba ATTRS{idVendor}=="0930", ATTRS{idProduct}=="130b", ENV{ID_MM_ERICSSON_MBM}="1" diff --git a/plugins/77-mm-longcheer-port-types.rules b/plugins/77-mm-longcheer-port-types.rules index 7317df7..ce0134b 100644 --- a/plugins/77-mm-longcheer-port-types.rules +++ b/plugins/77-mm-longcheer-port-types.rules @@ -6,6 +6,11 @@ # Alcatel One Touch X030 # MobiData MBD-200HU # ST Mobile Connect HSUPA USB Modem +# +# Most of these values were scraped from various Longcheer-based Windows +# driver .inf files. cmmdm.inf lists the actual data (ie PPP) ports, while +# cmser.inf lists the aux ports that may be either AT-capable or not but +# cannot be used for PPP. ACTION!="add|change", GOTO="mm_longcheer_port_types_end" @@ -18,6 +23,14 @@ GOTO="mm_longcheer_port_types_end" LABEL="mm_longcheer_vendorcheck" SUBSYSTEMS=="usb", ATTRS{bInterfaceNumber}=="?*", ENV{.MM_USBIFNUM}="$attr{bInterfaceNumber}" +ATTRS{idProduct}=="3197", ENV{.MM_USBIFNUM}=="00", ENV{ID_MM_LONGCHEER_PORT_TYPE_MODEM}="1" +ATTRS{idProduct}=="3197", ENV{.MM_USBIFNUM}=="01", ENV{ID_MM_LONGCHEER_PORT_TYPE_AUX}="1" +ATTRS{idProduct}=="3197", ENV{ID_MM_LONGCHEER_TAGGED}="1" + +ATTRS{idProduct}=="6000", ENV{.MM_USBIFNUM}=="00", ENV{ID_MM_LONGCHEER_PORT_TYPE_MODEM}="1" +ATTRS{idProduct}=="6000", ENV{.MM_USBIFNUM}=="02", ENV{ID_MM_LONGCHEER_PORT_TYPE_AUX}="1" +ATTRS{idProduct}=="6000", ENV{ID_MM_LONGCHEER_TAGGED}="1" + ATTRS{idProduct}=="6060", ENV{.MM_USBIFNUM}=="00", ENV{ID_MM_LONGCHEER_PORT_TYPE_MODEM}="1" ATTRS{idProduct}=="6060", ENV{.MM_USBIFNUM}=="02", ENV{ID_MM_LONGCHEER_PORT_TYPE_AUX}="1" ATTRS{idProduct}=="6060", ENV{ID_MM_LONGCHEER_TAGGED}="1" @@ -27,6 +40,127 @@ ATTRS{idProduct}=="6061", ENV{.MM_USBIFNUM}=="00", ENV{ID_MM_LONGCHEER_PORT_TYPE ATTRS{idProduct}=="6061", ENV{.MM_USBIFNUM}=="02", ENV{ID_MM_LONGCHEER_PORT_TYPE_AUX}="1" ATTRS{idProduct}=="6061", ENV{ID_MM_LONGCHEER_TAGGED}="1" +ATTRS{idProduct}=="7001", ENV{.MM_USBIFNUM}=="02", ENV{ID_MM_LONGCHEER_PORT_TYPE_MODEM}="1" +ATTRS{idProduct}=="7001", ENV{.MM_USBIFNUM}=="00", ENV{ID_MM_LONGCHEER_PORT_TYPE_AUX}="1" +ATTRS{idProduct}=="7001", ENV{.MM_USBIFNUM}=="03", ENV{ID_MM_LONGCHEER_PORT_TYPE_AUX}="1" +ATTRS{idProduct}=="7001", ENV{ID_MM_LONGCHEER_TAGGED}="1" + +ATTRS{idProduct}=="7002", ENV{.MM_USBIFNUM}=="02", ENV{ID_MM_LONGCHEER_PORT_TYPE_MODEM}="1" +ATTRS{idProduct}=="7002", ENV{.MM_USBIFNUM}=="00", ENV{ID_MM_LONGCHEER_PORT_TYPE_AUX}="1" +ATTRS{idProduct}=="7002", ENV{.MM_USBIFNUM}=="03", ENV{ID_MM_LONGCHEER_PORT_TYPE_AUX}="1" +ATTRS{idProduct}=="7002", ENV{.MM_USBIFNUM}=="04", ENV{ID_MM_LONGCHEER_PORT_TYPE_AUX}="1" +ATTRS{idProduct}=="7002", ENV{ID_MM_LONGCHEER_TAGGED}="1" + +ATTRS{idProduct}=="7101", ENV{.MM_USBIFNUM}=="04", ENV{ID_MM_LONGCHEER_PORT_TYPE_MODEM}="1" +ATTRS{idProduct}=="7101", ENV{.MM_USBIFNUM}=="00", ENV{ID_MM_LONGCHEER_PORT_TYPE_AUX}="1" +ATTRS{idProduct}=="7101", ENV{.MM_USBIFNUM}=="05", ENV{ID_MM_LONGCHEER_PORT_TYPE_AUX}="1" +ATTRS{idProduct}=="7101", ENV{ID_MM_LONGCHEER_TAGGED}="1" + +ATTRS{idProduct}=="7102", ENV{.MM_USBIFNUM}=="04", ENV{ID_MM_LONGCHEER_PORT_TYPE_MODEM}="1" +ATTRS{idProduct}=="7102", ENV{.MM_USBIFNUM}=="00", ENV{ID_MM_LONGCHEER_PORT_TYPE_AUX}="1" +ATTRS{idProduct}=="7102", ENV{.MM_USBIFNUM}=="05", ENV{ID_MM_LONGCHEER_PORT_TYPE_AUX}="1" +ATTRS{idProduct}=="7102", ENV{.MM_USBIFNUM}=="06", ENV{ID_MM_LONGCHEER_PORT_TYPE_AUX}="1" +ATTRS{idProduct}=="7102", ENV{ID_MM_LONGCHEER_TAGGED}="1" + +ATTRS{idProduct}=="8000", ENV{.MM_USBIFNUM}=="05", ENV{ID_MM_LONGCHEER_PORT_TYPE_MODEM}="1" +ATTRS{idProduct}=="8000", ENV{.MM_USBIFNUM}=="00", ENV{ID_MM_LONGCHEER_PORT_TYPE_AUX}="1" +ATTRS{idProduct}=="8000", ENV{.MM_USBIFNUM}=="02", ENV{ID_MM_LONGCHEER_PORT_TYPE_AUX}="1" +ATTRS{idProduct}=="8000", ENV{ID_MM_LONGCHEER_TAGGED}="1" + +ATTRS{idProduct}=="8001", ENV{.MM_USBIFNUM}=="04", ENV{ID_MM_LONGCHEER_PORT_TYPE_MODEM}="1" +ATTRS{idProduct}=="8001", ENV{.MM_USBIFNUM}=="00", ENV{ID_MM_LONGCHEER_PORT_TYPE_AUX}="1" +ATTRS{idProduct}=="8001", ENV{.MM_USBIFNUM}=="02", ENV{ID_MM_LONGCHEER_PORT_TYPE_AUX}="1" +ATTRS{idProduct}=="8001", ENV{ID_MM_LONGCHEER_TAGGED}="1" + +ATTRS{idProduct}=="8002", ENV{.MM_USBIFNUM}=="04", ENV{ID_MM_LONGCHEER_PORT_TYPE_MODEM}="1" +ATTRS{idProduct}=="8002", ENV{.MM_USBIFNUM}=="00", ENV{ID_MM_LONGCHEER_PORT_TYPE_AUX}="1" +ATTRS{idProduct}=="8002", ENV{.MM_USBIFNUM}=="02", ENV{ID_MM_LONGCHEER_PORT_TYPE_AUX}="1" +ATTRS{idProduct}=="8002", ENV{ID_MM_LONGCHEER_TAGGED}="1" + +# ChinaBird PL68 +ATTRS{idProduct}=="9000", ENV{.MM_USBIFNUM}=="03", ENV{ID_MM_LONGCHEER_PORT_TYPE_MODEM}="1" +ATTRS{idProduct}=="9000", ENV{.MM_USBIFNUM}=="01", ENV{ID_MM_LONGCHEER_PORT_TYPE_AUX}="1" +ATTRS{idProduct}=="9000", ENV{ID_MM_LONGCHEER_TAGGED}="1" + +ATTRS{idProduct}=="9001", ENV{.MM_USBIFNUM}=="02", ENV{ID_MM_LONGCHEER_PORT_TYPE_MODEM}="1" +ATTRS{idProduct}=="9001", ENV{.MM_USBIFNUM}=="00", ENV{ID_MM_LONGCHEER_PORT_TYPE_AUX}="1" +ATTRS{idProduct}=="9001", ENV{.MM_USBIFNUM}=="01", ENV{ID_MM_LONGCHEER_PORT_TYPE_AUX}="1" +ATTRS{idProduct}=="9001", ENV{ID_MM_LONGCHEER_TAGGED}="1" + +ATTRS{idProduct}=="9002", ENV{.MM_USBIFNUM}=="02", ENV{ID_MM_LONGCHEER_PORT_TYPE_MODEM}="1" +ATTRS{idProduct}=="9002", ENV{.MM_USBIFNUM}=="00", ENV{ID_MM_LONGCHEER_PORT_TYPE_AUX}="1" +ATTRS{idProduct}=="9002", ENV{.MM_USBIFNUM}=="01", ENV{ID_MM_LONGCHEER_PORT_TYPE_AUX}="1" +ATTRS{idProduct}=="9002", ENV{ID_MM_LONGCHEER_TAGGED}="1" + +ATTRS{idProduct}=="9003", ENV{.MM_USBIFNUM}=="03", ENV{ID_MM_LONGCHEER_PORT_TYPE_MODEM}="1" +ATTRS{idProduct}=="9003", ENV{.MM_USBIFNUM}=="00", ENV{ID_MM_LONGCHEER_PORT_TYPE_AUX}="1" +ATTRS{idProduct}=="9003", ENV{.MM_USBIFNUM}=="01", ENV{ID_MM_LONGCHEER_PORT_TYPE_AUX}="1" +ATTRS{idProduct}=="9003", ENV{.MM_USBIFNUM}=="02", ENV{ID_MM_LONGCHEER_PORT_TYPE_AUX}="1" +ATTRS{idProduct}=="9003", ENV{ID_MM_LONGCHEER_TAGGED}="1" + +ATTRS{idProduct}=="9004", ENV{.MM_USBIFNUM}=="01", ENV{ID_MM_LONGCHEER_PORT_TYPE_MODEM}="1" +ATTRS{idProduct}=="9004", ENV{.MM_USBIFNUM}=="00", ENV{ID_MM_LONGCHEER_PORT_TYPE_AUX}="1" +ATTRS{idProduct}=="9004", ENV{ID_MM_LONGCHEER_TAGGED}="1" + +ATTRS{idProduct}=="9005", ENV{.MM_USBIFNUM}=="01", ENV{ID_MM_LONGCHEER_PORT_TYPE_MODEM}="1" +ATTRS{idProduct}=="9005", ENV{.MM_USBIFNUM}=="00", ENV{ID_MM_LONGCHEER_PORT_TYPE_AUX}="1" +ATTRS{idProduct}=="9005", ENV{ID_MM_LONGCHEER_TAGGED}="1" + +ATTRS{idProduct}=="9010", ENV{.MM_USBIFNUM}=="03", ENV{ID_MM_LONGCHEER_PORT_TYPE_MODEM}="1" +ATTRS{idProduct}=="9010", ENV{.MM_USBIFNUM}=="00", ENV{ID_MM_LONGCHEER_PORT_TYPE_AUX}="1" +ATTRS{idProduct}=="9010", ENV{.MM_USBIFNUM}=="01", ENV{ID_MM_LONGCHEER_PORT_TYPE_AUX}="1" +ATTRS{idProduct}=="9010", ENV{ID_MM_LONGCHEER_TAGGED}="1" + +ATTRS{idProduct}=="9012", ENV{.MM_USBIFNUM}=="02", ENV{ID_MM_LONGCHEER_PORT_TYPE_MODEM}="1" +ATTRS{idProduct}=="9012", ENV{.MM_USBIFNUM}=="00", ENV{ID_MM_LONGCHEER_PORT_TYPE_AUX}="1" +ATTRS{idProduct}=="9012", ENV{.MM_USBIFNUM}=="01", ENV{ID_MM_LONGCHEER_PORT_TYPE_AUX}="1" +ATTRS{idProduct}=="9012", ENV{ID_MM_LONGCHEER_TAGGED}="1" + +ATTRS{idProduct}=="9020", ENV{.MM_USBIFNUM}=="03", ENV{ID_MM_LONGCHEER_PORT_TYPE_MODEM}="1" +ATTRS{idProduct}=="9020", ENV{.MM_USBIFNUM}=="00", ENV{ID_MM_LONGCHEER_PORT_TYPE_AUX}="1" +ATTRS{idProduct}=="9020", ENV{.MM_USBIFNUM}=="01", ENV{ID_MM_LONGCHEER_PORT_TYPE_AUX}="1" +ATTRS{idProduct}=="9020", ENV{ID_MM_LONGCHEER_TAGGED}="1" + +ATTRS{idProduct}=="9022", ENV{.MM_USBIFNUM}=="02", ENV{ID_MM_LONGCHEER_PORT_TYPE_MODEM}="1" +ATTRS{idProduct}=="9022", ENV{.MM_USBIFNUM}=="00", ENV{ID_MM_LONGCHEER_PORT_TYPE_AUX}="1" +ATTRS{idProduct}=="9022", ENV{.MM_USBIFNUM}=="01", ENV{ID_MM_LONGCHEER_PORT_TYPE_AUX}="1" +ATTRS{idProduct}=="9022", ENV{ID_MM_LONGCHEER_TAGGED}="1" + +# Zoom products +ATTRS{idProduct}=="9602", ENV{.MM_USBIFNUM}=="00", ENV{ID_MM_LONGCHEER_PORT_TYPE_MODEM}="1" +ATTRS{idProduct}=="9602", ENV{.MM_USBIFNUM}=="01", ENV{ID_MM_LONGCHEER_PORT_TYPE_AUX}="1" +ATTRS{idProduct}=="9602", ENV{.MM_USBIFNUM}=="02", ENV{ID_MM_LONGCHEER_PORT_TYPE_AUX}="1" +ATTRS{idProduct}=="9602", ENV{ID_MM_LONGCHEER_TAGGED}="1" + +ATTRS{idProduct}=="9603", ENV{.MM_USBIFNUM}=="02", ENV{ID_MM_LONGCHEER_PORT_TYPE_MODEM}="1" +ATTRS{idProduct}=="9603", ENV{.MM_USBIFNUM}=="00", ENV{ID_MM_LONGCHEER_PORT_TYPE_AUX}="1" +ATTRS{idProduct}=="9603", ENV{.MM_USBIFNUM}=="01", ENV{ID_MM_LONGCHEER_PORT_TYPE_AUX}="1" +ATTRS{idProduct}=="9603", ENV{ID_MM_LONGCHEER_TAGGED}="1" + +ATTRS{idProduct}=="9604", ENV{.MM_USBIFNUM}=="03", ENV{ID_MM_LONGCHEER_PORT_TYPE_MODEM}="1" +ATTRS{idProduct}=="9604", ENV{.MM_USBIFNUM}=="00", ENV{ID_MM_LONGCHEER_PORT_TYPE_AUX}="1" +ATTRS{idProduct}=="9604", ENV{.MM_USBIFNUM}=="01", ENV{ID_MM_LONGCHEER_PORT_TYPE_AUX}="1" +ATTRS{idProduct}=="9604", ENV{ID_MM_LONGCHEER_TAGGED}="1" + +ATTRS{idProduct}=="9605", ENV{.MM_USBIFNUM}=="03", ENV{ID_MM_LONGCHEER_PORT_TYPE_MODEM}="1" +ATTRS{idProduct}=="9605", ENV{.MM_USBIFNUM}=="00", ENV{ID_MM_LONGCHEER_PORT_TYPE_AUX}="1" +ATTRS{idProduct}=="9605", ENV{.MM_USBIFNUM}=="01", ENV{ID_MM_LONGCHEER_PORT_TYPE_AUX}="1" +ATTRS{idProduct}=="9605", ENV{.MM_USBIFNUM}=="02", ENV{ID_MM_LONGCHEER_PORT_TYPE_AUX}="1" +ATTRS{idProduct}=="9605", ENV{ID_MM_LONGCHEER_TAGGED}="1" + +ATTRS{idProduct}=="9606", ENV{.MM_USBIFNUM}=="04", ENV{ID_MM_LONGCHEER_PORT_TYPE_MODEM}="1" +ATTRS{idProduct}=="9606", ENV{.MM_USBIFNUM}=="00", ENV{ID_MM_LONGCHEER_PORT_TYPE_AUX}="1" +ATTRS{idProduct}=="9606", ENV{.MM_USBIFNUM}=="01", ENV{ID_MM_LONGCHEER_PORT_TYPE_AUX}="1" +ATTRS{idProduct}=="9606", ENV{.MM_USBIFNUM}=="02", ENV{ID_MM_LONGCHEER_PORT_TYPE_AUX}="1" +ATTRS{idProduct}=="9606", ENV{ID_MM_LONGCHEER_TAGGED}="1" + +ATTRS{idProduct}=="9607", ENV{.MM_USBIFNUM}=="04", ENV{ID_MM_LONGCHEER_PORT_TYPE_MODEM}="1" +ATTRS{idProduct}=="9607", ENV{.MM_USBIFNUM}=="00", ENV{ID_MM_LONGCHEER_PORT_TYPE_AUX}="1" +ATTRS{idProduct}=="9607", ENV{.MM_USBIFNUM}=="01", ENV{ID_MM_LONGCHEER_PORT_TYPE_AUX}="1" +ATTRS{idProduct}=="9607", ENV{.MM_USBIFNUM}=="02", ENV{ID_MM_LONGCHEER_PORT_TYPE_AUX}="1" +ATTRS{idProduct}=="9607", ENV{.MM_USBIFNUM}=="03", ENV{ID_MM_LONGCHEER_PORT_TYPE_AUX}="1" +ATTRS{idProduct}=="9607", ENV{ID_MM_LONGCHEER_TAGGED}="1" + GOTO="mm_longcheer_port_types_end" diff --git a/plugins/77-mm-simtech-port-types.rules b/plugins/77-mm-simtech-port-types.rules new file mode 100644 index 0000000..9ec047c --- /dev/null +++ b/plugins/77-mm-simtech-port-types.rules @@ -0,0 +1,29 @@ +# do not edit this file, it will be overwritten on update + +# Simtech makes modules that other companies rebrand, like: +# +# A-LINK 3GU +# +# Most of these values were scraped from various SimTech-based Windows +# driver .inf files. *mdm.inf lists the main command ports, while +# *ser.inf lists the aux ports that may be used for PPP. + + +ACTION!="add|change", GOTO="mm_simtech_port_types_end" +SUBSYSTEM!="tty", GOTO="mm_simtech_port_types_end" + +SUBSYSTEMS=="usb", ATTRS{idVendor}=="1e0e", GOTO="mm_alink_vendorcheck" +GOTO="mm_simtech_port_types_end" + +LABEL="mm_alink_vendorcheck" +SUBSYSTEMS=="usb", ATTRS{bInterfaceNumber}=="?*", ENV{.MM_USBIFNUM}="$attr{bInterfaceNumber}" + +ATTRS{idProduct}=="9200", ENV{.MM_USBIFNUM}=="03", ENV{ID_MM_SIMTECH_PORT_TYPE_MODEM}="1" +ATTRS{idProduct}=="9200", ENV{.MM_USBIFNUM}=="01", ENV{ID_MM_SIMTECH_PORT_TYPE_AUX}="1" +ATTRS{idProduct}=="9200", ENV{ID_MM_SIMTECH_TAGGED}="1" + +GOTO="mm_simtech_port_types_end" + + +LABEL="mm_simtech_port_types_end" + diff --git a/plugins/Makefile.am b/plugins/Makefile.am index a361358..8192653 100644 --- a/plugins/Makefile.am +++ b/plugins/Makefile.am @@ -11,7 +11,8 @@ pkglib_LTLIBRARIES = \ libmm-plugin-zte.la \ libmm-plugin-mbm.la \ libmm-plugin-longcheer.la \ - libmm-plugin-anydata.la + libmm-plugin-anydata.la \ + libmm-plugin-simtech.la # Generic @@ -169,7 +170,9 @@ libmm_plugin_novatel_la_SOURCES = \ mm-plugin-novatel.c \ mm-plugin-novatel.h \ mm-modem-novatel-gsm.c \ - mm-modem-novatel-gsm.h + mm-modem-novatel-gsm.h \ + mm-modem-novatel-cdma.c \ + mm-modem-novatel-cdma.h libmm_plugin_novatel_la_CPPFLAGS = \ $(MM_CFLAGS) \ @@ -221,7 +224,9 @@ libmm_plugin_zte_la_LDFLAGS = \ libmm_plugin_longcheer_la_SOURCES = \ mm-plugin-longcheer.c \ - mm-plugin-longcheer.h + mm-plugin-longcheer.h \ + mm-modem-longcheer-gsm.c \ + mm-modem-longcheer-gsm.h libmm_plugin_longcheer_la_CPPFLAGS = \ $(MM_CFLAGS) \ @@ -251,12 +256,31 @@ libmm_plugin_anydata_la_LDFLAGS = \ -module \ -avoid-version +# SimTech + +libmm_plugin_simtech_la_SOURCES = \ + mm-plugin-simtech.c \ + mm-plugin-simtech.h \ + mm-modem-simtech-gsm.c \ + mm-modem-simtech-gsm.h + +libmm_plugin_simtech_la_CPPFLAGS = \ + $(MM_CFLAGS) \ + $(GUDEV_CFLAGS) \ + -I$(top_srcdir)/src + +libmm_plugin_simtech_la_LDFLAGS = \ + $(GUDEV_LDFLAGS) \ + -module \ + -avoid-version + udevrulesdir = $(UDEV_BASE_DIR)/rules.d udevrules_DATA = \ 77-mm-ericsson-mbm.rules \ 77-mm-zte-port-types.rules \ - 77-mm-longcheer-port-types.rules + 77-mm-longcheer-port-types.rules \ + 77-mm-simtech-port-types.rules BUILT_SOURCES = \ mm-modem-gsm-hso-glue.h @@ -264,5 +288,6 @@ BUILT_SOURCES = \ CLEANFILES = $(BUILT_SOURCES) EXTRA_DIST = \ - $(udevrules_DATA) + $(udevrules_DATA) \ + mm-modem-option-utils.c diff --git a/plugins/mm-modem-anydata-cdma.c b/plugins/mm-modem-anydata-cdma.c index f6528ec..c7cca46 100644 --- a/plugins/mm-modem-anydata-cdma.c +++ b/plugins/mm-modem-anydata-cdma.c @@ -112,7 +112,7 @@ int_from_match_item (GMatchInfo *match_info, guint32 num, gint *val) } static void -evdo_state_done (MMSerialPort *port, +evdo_state_done (MMAtSerialPort *port, GString *response, GError *error, gpointer user_data) @@ -123,15 +123,8 @@ evdo_state_done (MMSerialPort *port, GRegex *r; GMatchInfo *match_info; - info->error = mm_modem_check_removed (info->modem, error); - if (info->error) { - if (info->modem) { - /* If HSTATE returned an error, assume the device is not EVDO capable - * or EVDO is not registered. - */ - mm_generic_cdma_query_reg_state_set_callback_evdo_state (info, MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN); - } - + if (error) { + /* Leave superclass' reg state alone if AT*HSTATE isn't supported */ mm_callback_info_schedule (info); return; } @@ -143,13 +136,8 @@ evdo_state_done (MMSerialPort *port, G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); if (!r) { /* Parse error; warn about it and assume EVDO is not available */ - g_warning ("AnyData(%s): failed to create EVDO state regex: (%d) %s", - __func__, - error ? error->code : -1, - error && error->message ? error->message : "(unknown)"); - mm_generic_cdma_query_reg_state_set_callback_evdo_state (info, MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN); - mm_callback_info_schedule (info); - return; + g_warning ("AnyDATA(%s): *HSTATE parse regex creation failed.", __func__); + goto done; } g_regex_match (r, reply, 0, &match_info); @@ -185,13 +173,13 @@ evdo_state_done (MMSerialPort *port, } } +done: mm_generic_cdma_query_reg_state_set_callback_evdo_state (info, reg_state); - mm_callback_info_schedule (info); } static void -state_done (MMSerialPort *port, +state_done (MMAtSerialPort *port, GString *response, GError *error, gpointer user_data) @@ -202,17 +190,8 @@ state_done (MMSerialPort *port, GRegex *r; GMatchInfo *match_info; - info->error = mm_modem_check_removed (info->modem, error); - if (info->error) { - if (info->modem) { - /* Assume if we got this far, we're registered even if an error - * occurred. We're not sure if all AnyData CDMA modems support - * the *STATE and *HSTATE commands. - */ - mm_generic_cdma_query_reg_state_set_callback_1x_state (info, MM_MODEM_CDMA_REGISTRATION_STATE_REGISTERED); - mm_generic_cdma_query_reg_state_set_callback_evdo_state (info, MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN); - } - + if (error) { + /* Leave superclass' reg state alone if AT*STATE isn't supported */ mm_callback_info_schedule (info); return; } @@ -223,9 +202,7 @@ state_done (MMSerialPort *port, r = g_regex_new ("\\s*(\\d+)\\s*,\\s*(\\d+)\\s*,\\s*(\\d+)\\s*,\\s*(\\d+)\\s*,\\s*(\\d+)\\s*,\\s*([^,\\)]*)\\s*,.*", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); if (!r) { - info->error = g_error_new_literal (MM_MODEM_ERROR, - MM_MODEM_ERROR_GENERAL, - "Could not parse sysinfo results (regex creation failed)."); + g_warning ("AnyDATA(%s): *STATE parse regex creation failed.", __func__); mm_callback_info_schedule (info); return; } @@ -254,7 +231,7 @@ state_done (MMSerialPort *port, reg_state = MM_MODEM_CDMA_REGISTRATION_STATE_REGISTERED; break; default: - g_message ("ANYDATA: unknown *STATE (%d); assuming no service.", val); + g_warning ("ANYDATA: unknown *STATE (%d); assuming no service.", val); /* fall through */ case 0: /* NO SERVICE */ break; @@ -265,35 +242,28 @@ state_done (MMSerialPort *port, mm_generic_cdma_query_reg_state_set_callback_1x_state (info, reg_state); /* Try for EVDO state too */ - mm_serial_port_queue_command (port, "*HSTATE?", 3, evdo_state_done, info); + mm_at_serial_port_queue_command (port, "*HSTATE?", 3, evdo_state_done, info); } static void query_registration_state (MMGenericCdma *cdma, + MMModemCdmaRegistrationState cur_cdma_state, + MMModemCdmaRegistrationState cur_evdo_state, MMModemCdmaRegistrationStateFn callback, gpointer user_data) { MMCallbackInfo *info; - MMSerialPort *primary, *secondary, *port; - - port = primary = mm_generic_cdma_get_port (cdma, MM_PORT_TYPE_PRIMARY); - secondary = mm_generic_cdma_get_port (cdma, MM_PORT_TYPE_SECONDARY); - - info = mm_generic_cdma_query_reg_state_callback_info_new (cdma, callback, user_data); + MMAtSerialPort *port; - if (mm_port_get_connected (MM_PORT (primary))) { - if (!secondary) { - info->error = g_error_new_literal (MM_MODEM_ERROR, MM_MODEM_ERROR_CONNECTED, - "Cannot get query registration state while connected"); - mm_callback_info_schedule (info); - return; - } + info = mm_generic_cdma_query_reg_state_callback_info_new (cdma, cur_cdma_state, cur_evdo_state, callback, user_data); - /* Use secondary port if primary is connected */ - port = secondary; + port = mm_generic_cdma_get_best_at_port (cdma, &info->error); + if (!port) { + mm_callback_info_schedule (info); + return; } - mm_serial_port_queue_command (port, "*STATE?", 3, state_done, info); + mm_at_serial_port_queue_command (port, "*STATE?", 3, state_done, info); } /*****************************************************************************/ @@ -310,22 +280,22 @@ grab_port (MMModem *modem, GRegex *regex; port = mm_generic_cdma_grab_port (MM_GENERIC_CDMA (modem), subsys, name, suggested_type, user_data, error); - if (port && MM_IS_SERIAL_PORT (port)) { + if (port && MM_IS_AT_SERIAL_PORT (port)) { /* Data state notifications */ /* Data call has connected */ regex = g_regex_new ("\\r\\n\\*ACTIVE:(.*)\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); - mm_serial_port_add_unsolicited_msg_handler (MM_SERIAL_PORT (port), regex, NULL, NULL, NULL); + mm_at_serial_port_add_unsolicited_msg_handler (MM_AT_SERIAL_PORT (port), regex, NULL, NULL, NULL); g_regex_unref (regex); /* Data call disconnected */ regex = g_regex_new ("\\r\\n\\*INACTIVE:(.*)\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); - mm_serial_port_add_unsolicited_msg_handler (MM_SERIAL_PORT (port), regex, NULL, NULL, NULL); + mm_at_serial_port_add_unsolicited_msg_handler (MM_AT_SERIAL_PORT (port), regex, NULL, NULL, NULL); g_regex_unref (regex); /* Modem is now dormant */ regex = g_regex_new ("\\r\\n\\*DORMANT:(.*)\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); - mm_serial_port_add_unsolicited_msg_handler (MM_SERIAL_PORT (port), regex, NULL, NULL, NULL); + mm_at_serial_port_add_unsolicited_msg_handler (MM_AT_SERIAL_PORT (port), regex, NULL, NULL, NULL); g_regex_unref (regex); /* Abnomral state notifications @@ -336,17 +306,17 @@ grab_port (MMModem *modem, /* Network acquisition fail */ regex = g_regex_new ("\\r\\n\\*OFFLINE:(.*)\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); - mm_serial_port_add_unsolicited_msg_handler (MM_SERIAL_PORT (port), regex, NULL, NULL, NULL); + mm_at_serial_port_add_unsolicited_msg_handler (MM_AT_SERIAL_PORT (port), regex, NULL, NULL, NULL); g_regex_unref (regex); /* Registration fail */ regex = g_regex_new ("\\r\\n\\*REGREQ:(.*)\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); - mm_serial_port_add_unsolicited_msg_handler (MM_SERIAL_PORT (port), regex, NULL, NULL, NULL); + mm_at_serial_port_add_unsolicited_msg_handler (MM_AT_SERIAL_PORT (port), regex, NULL, NULL, NULL); g_regex_unref (regex); /* Authentication fail */ regex = g_regex_new ("\\r\\n\\*AUTHREQ:(.*)\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); - mm_serial_port_add_unsolicited_msg_handler (MM_SERIAL_PORT (port), regex, NULL, NULL, NULL); + mm_at_serial_port_add_unsolicited_msg_handler (MM_AT_SERIAL_PORT (port), regex, NULL, NULL, NULL); g_regex_unref (regex); } diff --git a/plugins/mm-modem-gobi-gsm.c b/plugins/mm-modem-gobi-gsm.c index 7ea9f8f..3b9e9ec 100644 --- a/plugins/mm-modem-gobi-gsm.c +++ b/plugins/mm-modem-gobi-gsm.c @@ -23,6 +23,7 @@ #include "mm-errors.h" #include "mm-callback-info.h" #include "mm-modem-gsm-card.h" +#include "mm-at-serial-port.h" static void modem_init (MMModem *modem_class); static void modem_gsm_card_init (MMModemGsmCard *gsm_card_class); @@ -51,7 +52,7 @@ mm_modem_gobi_gsm_new (const char *device, /*****************************************************************************/ static void -get_string_done (MMSerialPort *port, +get_string_done (MMAtSerialPort *port, GString *response, GError *error, gpointer user_data) @@ -75,13 +76,16 @@ get_imsi (MMModemGsmCard *modem, MMModemStringFn callback, gpointer user_data) { - MMSerialPort *primary; + MMAtSerialPort *port; MMCallbackInfo *info; info = mm_callback_info_string_new (MM_MODEM (modem), callback, user_data); - primary = mm_generic_gsm_get_port (MM_GENERIC_GSM (modem), MM_PORT_TYPE_PRIMARY); - g_assert (primary); - mm_serial_port_queue_command_cached (primary, "+CIMI", 3, get_string_done, info); + + port = mm_generic_gsm_get_best_at_port (MM_GENERIC_GSM (modem), &info->error); + if (port) + mm_at_serial_port_queue_command_cached (port, "+CIMI", 3, get_string_done, info); + else + mm_callback_info_schedule (info); } static void diff --git a/plugins/mm-modem-hso.c b/plugins/mm-modem-hso.c index f1295e2..1fd4633 100644 --- a/plugins/mm-modem-hso.c +++ b/plugins/mm-modem-hso.c @@ -63,6 +63,9 @@ typedef struct { MMCallbackInfo *connect_pending_data; guint connect_pending_id; + char *username; + char *password; + guint32 auth_idx; } MMModemHsoPrivate; @@ -85,62 +88,122 @@ mm_modem_hso_new (const char *device, NULL)); } -#define IGNORE_ERRORS_TAG "ignore-errors" +#include "mm-modem-option-utils.c" + +/*****************************************************************************/ + +static gint +hso_get_cid (MMModemHso *self) +{ + gint cid; + + cid = mm_generic_gsm_get_cid (MM_GENERIC_GSM (self)); + if (cid < 0) { + g_warn_if_fail (cid >= 0); + cid = 0; + } + + return cid; +} static void -hso_call_control_done (MMSerialPort *port, - GString *response, - GError *error, - gpointer user_data) +auth_done (MMAtSerialPort *port, + GString *response, + GError *error, + gpointer user_data) { MMCallbackInfo *info = (MMCallbackInfo *) user_data; + MMModemHso *self = MM_MODEM_HSO (info->modem); + MMModemHsoPrivate *priv = MM_MODEM_HSO_GET_PRIVATE (self); - if (error && !mm_callback_info_get_data (info, IGNORE_ERRORS_TAG)) - info->error = g_error_copy (error); + if (error) { + priv->auth_idx++; + if (auth_commands[priv->auth_idx]) { + /* Try the next auth command */ + _internal_hso_modem_authenticate (self, info); + return; + } else + info->error = g_error_copy (error); + } + /* Reset to 0 so something gets tried the next connection */ + priv->auth_idx = 0; mm_callback_info_schedule (info); } -static guint32 -hso_get_cid (MMModemHso *self) +static void +_internal_hso_modem_authenticate (MMModemHso *self, MMCallbackInfo *info) { - guint32 cid; + MMModemHsoPrivate *priv = MM_MODEM_HSO_GET_PRIVATE (self); + MMAtSerialPort *primary; + gint cid; + char *command; - cid = mm_generic_gsm_get_cid (MM_GENERIC_GSM (self)); - if (cid == 0) - cid = 1; + primary = mm_generic_gsm_get_at_port (MM_GENERIC_GSM (self), MM_PORT_TYPE_PRIMARY); + g_assert (primary); - return cid; + cid = hso_get_cid (self); + g_warn_if_fail (cid >= 0); + + /* Both user and password are required; otherwise firmware returns an error */ + if (!priv->username || !priv->password) + command = g_strdup_printf ("%s=%d,0", auth_commands[priv->auth_idx], cid); + else { + command = g_strdup_printf ("%s=%d,1,\"%s\",\"%s\"", + auth_commands[priv->auth_idx], + cid, + priv->password ? priv->password : "", + priv->username ? priv->username : ""); + + } + + mm_at_serial_port_queue_command (primary, command, 3, auth_done, info); + g_free (command); } -static void -hso_call_control (MMModemHso *self, - gboolean activate, - gboolean ignore_errors, - MMModemFn callback, - gpointer user_data) +void +mm_hso_modem_authenticate (MMModemHso *self, + const char *username, + const char *password, + MMModemFn callback, + gpointer user_data) { + MMModemHsoPrivate *priv; MMCallbackInfo *info; - char *command; - MMSerialPort *primary; + + g_return_if_fail (self != NULL); + g_return_if_fail (MM_IS_MODEM_HSO (self)); + g_return_if_fail (callback != NULL); info = mm_callback_info_new (MM_MODEM (self), callback, user_data); - mm_callback_info_set_data (info, IGNORE_ERRORS_TAG, GUINT_TO_POINTER (ignore_errors), NULL); - command = g_strdup_printf ("AT_OWANCALL=%d,%d,1", hso_get_cid (self), activate ? 1 : 0); - primary = mm_generic_gsm_get_port (MM_GENERIC_GSM (self), MM_PORT_TYPE_PRIMARY); - g_assert (primary); - mm_serial_port_queue_command (primary, command, 3, hso_call_control_done, info); - g_free (command); + priv = MM_MODEM_HSO_GET_PRIVATE (self); + + g_free (priv->username); + priv->username = (username && strlen (username)) ? g_strdup (username) : NULL; + + g_free (priv->password); + priv->password = (password && strlen (password)) ? g_strdup (password) : NULL; + + _internal_hso_modem_authenticate (self, info); } +/*****************************************************************************/ + static void connect_pending_done (MMModemHso *self) { MMModemHsoPrivate *priv = MM_MODEM_HSO_GET_PRIVATE (self); + GError *error = NULL; if (priv->connect_pending_data) { - mm_callback_info_schedule (priv->connect_pending_data); + if (priv->connect_pending_data->error) { + error = priv->connect_pending_data->error; + priv->connect_pending_data->error = NULL; + } + + /* Complete the connect */ + mm_generic_gsm_connect_complete (MM_GENERIC_GSM (self), error, priv->connect_pending_data); priv->connect_pending_data = NULL; } @@ -150,177 +213,175 @@ connect_pending_done (MMModemHso *self) } } -static gboolean -hso_connect_timed_out (gpointer data) +static void +connection_enabled (MMAtSerialPort *port, + GMatchInfo *match_info, + gpointer user_data) { - MMModemHsoPrivate *priv = MM_MODEM_HSO_GET_PRIVATE (data); + MMModemHso *self = MM_MODEM_HSO (user_data); + MMModemHsoPrivate *priv = MM_MODEM_HSO_GET_PRIVATE (self); + char *str; + + str = g_match_info_fetch (match_info, 2); + if (str[0] == '1') + connect_pending_done (self); + else if (str[0] == '3') { + MMCallbackInfo *info = priv->connect_pending_data; - priv->connect_pending_data->error = g_error_new_literal (MM_SERIAL_ERROR, - MM_SERIAL_RESPONSE_TIMEOUT, - "Connection timed out"); - connect_pending_done (MM_MODEM_HSO (data)); + if (info) { + info->error = g_error_new_literal (MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL, + "Call setup failed"); + } - return FALSE; + connect_pending_done (self); + } else if (str[0] == '0') { + /* FIXME: disconnected. do something when we have modem status signals */ + } + + g_free (str); } +/*****************************************************************************/ + +#define IGNORE_ERRORS_TAG "ignore-errors" + static void -hso_enabled (MMModem *modem, - GError *error, - gpointer user_data) +hso_call_control_done (MMAtSerialPort *port, + GString *response, + GError *error, + gpointer user_data) { MMCallbackInfo *info = (MMCallbackInfo *) user_data; - if (error) { + if (error && !mm_callback_info_get_data (info, IGNORE_ERRORS_TAG)) info->error = g_error_copy (error); - mm_callback_info_schedule (info); - } else { - MMModemHsoPrivate *priv = MM_MODEM_HSO_GET_PRIVATE (modem); - GSource *source; - source = g_timeout_source_new_seconds (30); - g_source_set_closure (source, g_cclosure_new_object (G_CALLBACK (hso_connect_timed_out), G_OBJECT (modem))); - g_source_attach (source, NULL); - priv->connect_pending_data = info; - priv->connect_pending_id = g_source_get_id (source); - g_source_unref (source); - } + mm_callback_info_schedule (info); } static void -clear_old_context (MMModem *modem, - GError *error, - gpointer user_data) +hso_call_control (MMModemHso *self, + gboolean activate, + gboolean ignore_errors, + MMModemFn callback, + gpointer user_data) { - MMCallbackInfo *info = (MMCallbackInfo *) user_data; + MMCallbackInfo *info; + char *command; + MMAtSerialPort *primary; - if (error) { - info->error = g_error_copy (error); - mm_callback_info_schedule (info); - } else { - /* Success, activate the PDP context and start the data session */ - hso_call_control (MM_MODEM_HSO (modem), TRUE, FALSE, hso_enabled, info); - } + info = mm_callback_info_new (MM_MODEM (self), callback, user_data); + mm_callback_info_set_data (info, IGNORE_ERRORS_TAG, GUINT_TO_POINTER (ignore_errors), NULL); + + command = g_strdup_printf ("AT_OWANCALL=%d,%d,1", hso_get_cid (self), activate ? 1 : 0); + primary = mm_generic_gsm_get_at_port (MM_GENERIC_GSM (self), MM_PORT_TYPE_PRIMARY); + g_assert (primary); + mm_at_serial_port_queue_command (primary, command, 3, hso_call_control_done, info); + g_free (command); } static void -auth_done (MMSerialPort *port, - GString *response, - GError *error, - gpointer user_data) +timeout_done (MMModem *modem, + GError *error, + gpointer user_data) { - MMCallbackInfo *info = (MMCallbackInfo *) user_data; - MMModemHso *self = MM_MODEM_HSO (info->modem); - MMModemHsoPrivate *priv = MM_MODEM_HSO_GET_PRIVATE (self); - - if (error) { - priv->auth_idx++; - if (auth_commands[priv->auth_idx]) { - /* Try the next auth command */ - _internal_hso_modem_authenticate (self, info); - } else { - /* Reset to 0 so that something gets tried for the next connection */ - priv->auth_idx = 0; - - info->error = g_error_copy (error); - mm_callback_info_schedule (info); - } - } else { - priv->auth_idx = 0; - - /* success, kill any existing connections first */ - hso_call_control (self, FALSE, TRUE, clear_old_context, info); - } + if (modem) + connect_pending_done (MM_MODEM_HSO (modem)); } -static void -_internal_hso_modem_authenticate (MMModemHso *self, MMCallbackInfo *info) +static gboolean +hso_connect_timed_out (gpointer data) { + MMModemHso *self = MM_MODEM_HSO (data); MMModemHsoPrivate *priv = MM_MODEM_HSO_GET_PRIVATE (self); - MMSerialPort *primary; - guint32 cid; - char *command; - const char *username, *password; - - primary = mm_generic_gsm_get_port (MM_GENERIC_GSM (self), MM_PORT_TYPE_PRIMARY); - g_assert (primary); - - cid = hso_get_cid (self); - - username = mm_callback_info_get_data (info, "username"); - password = mm_callback_info_get_data (info, "password"); + MMCallbackInfo *info = priv->connect_pending_data; - if (!username && !password) - command = g_strdup_printf ("%s=%d,0", auth_commands[priv->auth_idx], cid); - else { - command = g_strdup_printf ("%s=%d,1,\"%s\",\"%s\"", - auth_commands[priv->auth_idx], - cid, - password ? password : "", - username ? username : ""); + priv->connect_pending_id = 0; + if (info) { + info->error = g_error_new_literal (MM_SERIAL_ERROR, + MM_SERIAL_ERROR_RESPONSE_TIMEOUT, + "Connection timed out"); } - mm_serial_port_queue_command (primary, command, 3, auth_done, info); - g_free (command); + hso_call_control (self, FALSE, TRUE, timeout_done, self); + return FALSE; } -void -mm_hso_modem_authenticate (MMModemHso *self, - const char *username, - const char *password, - MMModemFn callback, - gpointer user_data) +static void +hso_enabled (MMModem *modem, + GError *error, + gpointer user_data) { - MMCallbackInfo *info; - - g_return_if_fail (MM_IS_MODEM_HSO (self)); - g_return_if_fail (callback != NULL); + MMCallbackInfo *info = (MMCallbackInfo *) user_data; + GError *tmp_error; - info = mm_callback_info_new (MM_MODEM (self), callback, user_data); - if (username) - mm_callback_info_set_data (info, "username", g_strdup (username), g_free); - if (password) - mm_callback_info_set_data (info, "password", g_strdup (password), g_free); + tmp_error = mm_modem_check_removed (modem, error); + if (tmp_error) { + mm_generic_gsm_connect_complete (MM_GENERIC_GSM (modem), tmp_error, info); + g_clear_error (&tmp_error); + } else { + MMModemHsoPrivate *priv = MM_MODEM_HSO_GET_PRIVATE (modem); - _internal_hso_modem_authenticate (self, info); + priv->connect_pending_data = info; + priv->connect_pending_id = g_timeout_add_seconds (30, hso_connect_timed_out, modem); + } } -/*****************************************************************************/ - static void -enable_done (MMModem *modem, GError *error, gpointer user_data) +old_context_clear_done (MMModem *modem, + GError *error, + gpointer user_data) { MMCallbackInfo *info = (MMCallbackInfo *) user_data; + GError *tmp_error; - mm_generic_gsm_enable_complete (MM_GENERIC_GSM (modem), error, info); + tmp_error = mm_modem_check_removed (modem, error); + if (tmp_error) { + mm_generic_gsm_connect_complete (MM_GENERIC_GSM (modem), tmp_error, info); + g_clear_error (&tmp_error); + } else { + /* Success, activate the PDP context and start the data session */ + hso_call_control (MM_MODEM_HSO (modem), TRUE, FALSE, hso_enabled, info); + } } static void -parent_enable_done (MMModem *modem, GError *error, gpointer user_data) +connect_auth_done (MMModem *modem, + GError *error, + gpointer user_data) { MMCallbackInfo *info = (MMCallbackInfo *) user_data; - MMGenericGsm *self = MM_GENERIC_GSM (modem); + GError *tmp_error; - if (error) { - mm_generic_gsm_enable_complete (self, error, info); - return; + tmp_error = mm_modem_check_removed (modem, error); + if (tmp_error) { + mm_generic_gsm_connect_complete (MM_GENERIC_GSM (modem), tmp_error, info); + g_clear_error (&tmp_error); + } else { + /* Now connect; kill any existing connections first */ + hso_call_control (MM_MODEM_HSO (modem), FALSE, TRUE, old_context_clear_done, info); } - - /* HSO needs manual PIN checking */ - mm_generic_gsm_check_pin (self, enable_done, info); } static void -enable (MMModem *modem, MMModemFn callback, gpointer user_data) +do_connect (MMModem *modem, + const char *number, + MMModemFn callback, + gpointer user_data) { - MMModem *parent_modem_iface; - MMCallbackInfo *info; + MMModemHso *self = MM_MODEM_HSO (modem); + MMCallbackInfo *auth_info, *connect_info; - info = mm_callback_info_new (modem, callback, user_data); - parent_modem_iface = g_type_interface_peek_parent (MM_MODEM_GET_INTERFACE (info->modem)); - parent_modem_iface->enable (info->modem, parent_enable_done, info); + mm_modem_set_state (modem, MM_MODEM_STATE_CONNECTING, MM_MODEM_STATE_REASON_NONE); + + connect_info = mm_callback_info_new (modem, callback, user_data); + auth_info = mm_callback_info_new (modem, connect_auth_done, connect_info); + _internal_hso_modem_authenticate (self, auth_info); } +/*****************************************************************************/ + static void parent_disable_done (MMModem *modem, GError *error, gpointer user_data) { @@ -345,32 +406,56 @@ disable_done (MMModem *modem, } static void -disable (MMModem *modem, - MMModemFn callback, - gpointer user_data) +unsolicited_disable_done (MMModem *modem, + GError *error, + gpointer user_data) { - MMCallbackInfo *info; + MMCallbackInfo *info = user_data; - mm_generic_gsm_pending_registration_stop (MM_GENERIC_GSM (modem)); + /* Handle modem removal, but ignore other errors */ + if (g_error_matches (error, MM_MODEM_ERROR, MM_MODEM_ERROR_REMOVED)) + info->error = g_error_copy (error); + else if (!modem) { + info->error = g_error_new_literal (MM_MODEM_ERROR, + MM_MODEM_ERROR_REMOVED, + "The modem was removed."); + } - info = mm_callback_info_new (modem, callback, user_data); + if (info->error) { + mm_callback_info_schedule (info); + return; + } - /* Kill any existing connection */ - hso_call_control (MM_MODEM_HSO (modem), FALSE, TRUE, disable_done, info); + /* Otherwise, kill any existing connection */ + if (mm_generic_gsm_get_cid (MM_GENERIC_GSM (modem)) >= 0) + hso_call_control (MM_MODEM_HSO (modem), FALSE, TRUE, disable_done, info); + else + disable_done (modem, NULL, info); } static void -do_connect (MMModem *modem, - const char *number, - MMModemFn callback, - gpointer user_data) +disable (MMModem *modem, + MMModemFn callback, + gpointer user_data) { + MMModemHso *self = MM_MODEM_HSO (modem); + MMModemHsoPrivate *priv = MM_MODEM_HSO_GET_PRIVATE (self); MMCallbackInfo *info; + mm_generic_gsm_pending_registration_stop (MM_GENERIC_GSM (modem)); + + g_free (priv->username); + priv->username = NULL; + g_free (priv->password); + priv->password = NULL; + info = mm_callback_info_new (modem, callback, user_data); - mm_callback_info_schedule (info); + + /* Turn off unsolicited messages so they don't pile up in the modem */ + option_change_unsolicited_messages (MM_GENERIC_GSM (modem), FALSE, unsolicited_disable_done, info); } +/*****************************************************************************/ static void free_dns_array (gpointer data) @@ -390,7 +475,7 @@ ip4_config_invoke (MMCallbackInfo *info) } static void -get_ip4_config_done (MMSerialPort *port, +get_ip4_config_done (MMAtSerialPort *port, GString *response, GError *error, gpointer user_data) @@ -400,7 +485,7 @@ get_ip4_config_done (MMSerialPort *port, GArray *dns_array; int i; guint32 tmp; - guint cid; + gint cid; if (error) { info->error = g_error_copy (error); @@ -421,7 +506,7 @@ get_ip4_config_done (MMSerialPort *port, errno = 0; num = strtol (*iter, NULL, 10); - if (errno != 0 || num < 0 || (guint) num != cid) { + if (errno != 0 || num < 0 || (gint) num != cid) { info->error = g_error_new (MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL, "Unknown CID in OWANDATA response (" "got %d, expected %d)", (guint) num, cid); @@ -453,28 +538,61 @@ get_ip4_config (MMModem *modem, { MMCallbackInfo *info; char *command; - MMSerialPort *primary; + MMAtSerialPort *primary; info = mm_callback_info_new_full (modem, ip4_config_invoke, G_CALLBACK (callback), user_data); command = g_strdup_printf ("AT_OWANDATA=%d", hso_get_cid (MM_MODEM_HSO (modem))); - primary = mm_generic_gsm_get_port (MM_GENERIC_GSM (modem), MM_PORT_TYPE_PRIMARY); + primary = mm_generic_gsm_get_at_port (MM_GENERIC_GSM (modem), MM_PORT_TYPE_PRIMARY); g_assert (primary); - mm_serial_port_queue_command (primary, command, 3, get_ip4_config_done, info); + mm_at_serial_port_queue_command (primary, command, 3, get_ip4_config_done, info); g_free (command); } +/*****************************************************************************/ + static void -disconnect (MMModem *modem, - MMModemFn callback, - gpointer user_data) +disconnect_owancall_done (MMAtSerialPort *port, + GString *response, + GError *error, + gpointer user_data) +{ + mm_callback_info_schedule ((MMCallbackInfo *) user_data); +} + +static void +do_disconnect (MMGenericGsm *gsm, + gint cid, + MMModemFn callback, + gpointer user_data) { MMCallbackInfo *info; - MMSerialPort *primary; + MMAtSerialPort *primary; + char *command; - info = mm_callback_info_new (modem, callback, user_data); - primary = mm_generic_gsm_get_port (MM_GENERIC_GSM (modem), MM_PORT_TYPE_PRIMARY); + info = mm_callback_info_new (MM_MODEM (gsm), callback, user_data); + + primary = mm_generic_gsm_get_at_port (gsm, MM_PORT_TYPE_PRIMARY); g_assert (primary); - mm_serial_port_queue_command (primary, "AT_OWANCALL=1,0,0", 3, NULL, info); + + command = g_strdup_printf ("AT_OWANCALL=%d,0,0", cid); + mm_at_serial_port_queue_command (primary, command, 3, disconnect_owancall_done, info); + g_free (command); +} + +/*****************************************************************************/ + +static void +real_do_enable_power_up_done (MMGenericGsm *gsm, + GString *response, + GError *error, + MMCallbackInfo *info) +{ + /* Enable Option unsolicited messages */ + if (gsm && !error) + option_change_unsolicited_messages (gsm, TRUE, NULL, NULL); + + /* Chain up to parent */ + MM_GENERIC_GSM_CLASS (mm_modem_hso_parent_class)->do_enable_power_up_done (gsm, response, error, info); } /*****************************************************************************/ @@ -507,47 +625,11 @@ impl_hso_authenticate (MMModemHso *self, mm_hso_modem_authenticate (self, username, password, impl_hso_auth_done, context); } -static void -connection_enabled (MMSerialPort *port, - GMatchInfo *info, - gpointer user_data) -{ - MMModemHso *self = MM_MODEM_HSO (user_data); - MMModemHsoPrivate *priv = MM_MODEM_HSO_GET_PRIVATE (self); - char *str; - - str = g_match_info_fetch (info, 2); - if (str[0] == '1') - connect_pending_done (self); - else if (str[0] == '3') { - MMCallbackInfo *cb_info = priv->connect_pending_data; - - if (cb_info) - cb_info->error = g_error_new_literal (MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL, - "Call setup failed"); - - connect_pending_done (self); - } else if (str[0] == '0') - /* FIXME: disconnected. do something when we have modem status signals */ - ; - - g_free (str); -} - /*****************************************************************************/ -/* MMModemSimple interface */ - -typedef enum { - SIMPLE_STATE_BEGIN = 0, - SIMPLE_STATE_PARENT_CONNECT, - SIMPLE_STATE_AUTHENTICATE, - SIMPLE_STATE_DONE -} SimpleState; static const char * -simple_get_string_property (MMCallbackInfo *info, const char *name, GError **error) +hso_simple_get_string_property (GHashTable *properties, const char *name, GError **error) { - GHashTable *properties = (GHashTable *) mm_callback_info_get_data (info, "simple-connect-properties"); GValue *value; value = (GValue *) g_hash_table_lookup (properties, name); @@ -565,60 +647,47 @@ simple_get_string_property (MMCallbackInfo *info, const char *name, GError **err } static void -simple_state_machine (MMModem *modem, GError *error, gpointer user_data) +simple_connect (MMModemSimple *simple, + GHashTable *properties, + MMModemFn callback, + gpointer user_data) { + MMModemHsoPrivate *priv = MM_MODEM_HSO_GET_PRIVATE (simple); MMCallbackInfo *info = (MMCallbackInfo *) user_data; MMModemSimple *parent_iface; - const char *username; - const char *password; - GHashTable *properties = (GHashTable *) mm_callback_info_get_data (info, "simple-connect-properties"); - SimpleState state = GPOINTER_TO_UINT (mm_callback_info_get_data (info, "simple-connect-state")); - - if (error) { - info->error = g_error_copy (error); - goto out; - } - switch (state) { - case SIMPLE_STATE_BEGIN: - state = SIMPLE_STATE_PARENT_CONNECT; - parent_iface = g_type_interface_peek_parent (MM_MODEM_SIMPLE_GET_INTERFACE (modem)); - parent_iface->connect (MM_MODEM_SIMPLE (modem), properties, simple_state_machine, info); - break; - case SIMPLE_STATE_PARENT_CONNECT: - state = SIMPLE_STATE_AUTHENTICATE; - username = simple_get_string_property (info, "username", &info->error); - password = simple_get_string_property (info, "password", &info->error); - mm_hso_modem_authenticate (MM_MODEM_HSO (modem), username, password, simple_state_machine, info); - break; - case SIMPLE_STATE_AUTHENTICATE: - state = SIMPLE_STATE_DONE; - break; - default: - break; - } + priv->username = g_strdup (hso_simple_get_string_property (properties, "username", NULL)); + priv->password = g_strdup (hso_simple_get_string_property (properties, "password", NULL)); - out: - if (info->error || state == SIMPLE_STATE_DONE) - mm_callback_info_schedule (info); - else - mm_callback_info_set_data (info, "simple-connect-state", GUINT_TO_POINTER (state), NULL); + parent_iface = g_type_interface_peek_parent (MM_MODEM_SIMPLE_GET_INTERFACE (simple)); + parent_iface->connect (MM_MODEM_SIMPLE (simple), properties, callback, info); } +/*****************************************************************************/ + static void -simple_connect (MMModemSimple *simple, - GHashTable *properties, - MMModemFn callback, - gpointer user_data) +get_allowed_mode (MMGenericGsm *gsm, + MMModemUIntFn callback, + gpointer user_data) { - MMCallbackInfo *info; + option_get_allowed_mode (gsm, callback, user_data); +} - info = mm_callback_info_new (MM_MODEM (simple), callback, user_data); - mm_callback_info_set_data (info, "simple-connect-properties", - g_hash_table_ref (properties), - (GDestroyNotify) g_hash_table_unref); +static void +set_allowed_mode (MMGenericGsm *gsm, + MMModemGsmAllowedMode mode, + MMModemFn callback, + gpointer user_data) +{ + option_set_allowed_mode (gsm, mode, callback, user_data); +} - simple_state_machine (MM_MODEM (simple), NULL, info); +static void +get_access_technology (MMGenericGsm *gsm, + MMModemUIntFn callback, + gpointer user_data) +{ + option_get_access_technology (gsm, callback, user_data); } /*****************************************************************************/ @@ -676,17 +745,20 @@ grab_port (MMModem *modem, if (!port) goto out; - if (MM_IS_SERIAL_PORT (port)) { + if (MM_IS_AT_SERIAL_PORT (port)) { g_object_set (G_OBJECT (port), MM_SERIAL_PORT_SEND_DELAY, (guint64) 10000, NULL); if (ptype == MM_PORT_TYPE_PRIMARY) { GRegex *regex; - mm_generic_gsm_set_unsolicited_registration (gsm, TRUE); - regex = g_regex_new ("_OWANCALL: (\\d),\\s*(\\d)\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); - mm_serial_port_add_unsolicited_msg_handler (MM_SERIAL_PORT (port), regex, connection_enabled, modem, NULL); + mm_at_serial_port_add_unsolicited_msg_handler (MM_AT_SERIAL_PORT (port), regex, connection_enabled, modem, NULL); + g_regex_unref (regex); + + regex = g_regex_new ("\\r\\n\\+PACSP0\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); + mm_at_serial_port_add_unsolicited_msg_handler (MM_AT_SERIAL_PORT (port), regex, NULL, NULL, NULL); g_regex_unref (regex); } + option_register_unsolicted_handlers (gsm, MM_AT_SERIAL_PORT (port)); } out: @@ -712,19 +784,23 @@ modem_simple_init (MMModemSimple *class) static void modem_init (MMModem *modem_class) { - modem_class->enable = enable; modem_class->disable = disable; modem_class->connect = do_connect; modem_class->get_ip4_config = get_ip4_config; - modem_class->disconnect = disconnect; modem_class->grab_port = grab_port; } static void finalize (GObject *object) { + MMModemHso *self = MM_MODEM_HSO (object); + MMModemHsoPrivate *priv = MM_MODEM_HSO_GET_PRIVATE (self); + /* Clear the pending connection if necessary */ - connect_pending_done (MM_MODEM_HSO (object)); + connect_pending_done (self); + + g_free (priv->username); + g_free (priv->password); G_OBJECT_CLASS (mm_modem_hso_parent_class)->finalize (object); } @@ -733,11 +809,17 @@ static void mm_modem_hso_class_init (MMModemHsoClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); + MMGenericGsmClass *gsm_class = MM_GENERIC_GSM_CLASS (klass); mm_modem_hso_parent_class = g_type_class_peek_parent (klass); g_type_class_add_private (object_class, sizeof (MMModemHsoPrivate)); /* Virtual methods */ object_class->finalize = finalize; + gsm_class->do_disconnect = do_disconnect; + gsm_class->do_enable_power_up_done = real_do_enable_power_up_done; + gsm_class->set_allowed_mode = set_allowed_mode; + gsm_class->get_allowed_mode = get_allowed_mode; + gsm_class->get_access_technology = get_access_technology; } diff --git a/plugins/mm-modem-huawei-cdma.c b/plugins/mm-modem-huawei-cdma.c index 3b63a48..523578f 100644 --- a/plugins/mm-modem-huawei-cdma.c +++ b/plugins/mm-modem-huawei-cdma.c @@ -41,16 +41,26 @@ mm_modem_huawei_cdma_new (const char *device, gboolean evdo_rev0, gboolean evdo_revA) { + gboolean try_css = TRUE; + g_return_val_if_fail (device != NULL, NULL); g_return_val_if_fail (driver != NULL, NULL); g_return_val_if_fail (plugin != NULL, NULL); + /* Don't use AT+CSS on EVDO-capable hardware for determining registration + * status, because often the device will have only an EVDO connection and + * AT+CSS won't necessarily report EVDO registration status, only 1X. + */ + if (evdo_rev0 || evdo_revA) + try_css = FALSE; + return MM_MODEM (g_object_new (MM_TYPE_MODEM_HUAWEI_CDMA, MM_MODEM_MASTER_DEVICE, device, MM_MODEM_DRIVER, driver, MM_MODEM_PLUGIN, plugin, MM_GENERIC_CDMA_EVDO_REV0, evdo_rev0, MM_GENERIC_CDMA_EVDO_REVA, evdo_revA, + MM_GENERIC_CDMA_REGISTRATION_TRY_CSS, try_css, NULL)); } @@ -72,7 +82,7 @@ parse_quality (const char *str, const char *detail) } static void -handle_1x_quality_change (MMSerialPort *port, +handle_1x_quality_change (MMAtSerialPort *port, GMatchInfo *match_info, gpointer user_data) { @@ -89,9 +99,9 @@ handle_1x_quality_change (MMSerialPort *port, } static void -handle_evdo_quality_change (MMSerialPort *port, - GMatchInfo *match_info, - gpointer user_data) +handle_evdo_quality_change (MMAtSerialPort *port, + GMatchInfo *match_info, + gpointer user_data) { MMModemHuaweiCdma *self = MM_MODEM_HUAWEI_CDMA (user_data); char *str; @@ -142,7 +152,7 @@ uint_from_match_item (GMatchInfo *match_info, guint32 num, guint32 *val) } static void -sysinfo_done (MMSerialPort *port, +sysinfo_done (MMAtSerialPort *port, GString *response, GError *error, gpointer user_data) @@ -151,12 +161,10 @@ sysinfo_done (MMSerialPort *port, GRegex *r; GMatchInfo *match_info; const char *reply; - gboolean success = FALSE; if (error) { - info->error = g_error_copy (error); - mm_callback_info_schedule (info); - return; + /* Leave superclass' reg state alone if AT^SYSINFO isn't supported */ + goto done; } reply = strip_response (response->str, "^SYSINFO:"); @@ -165,9 +173,7 @@ sysinfo_done (MMSerialPort *port, r = g_regex_new ("\\s*(\\d+)\\s*,\\s*(\\d+)\\s*,\\s*(\\d+)\\s*,\\s*(\\d+)\\s*,\\s*(\\d+)", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); if (!r) { - info->error = g_error_new_literal (MM_MODEM_ERROR, - MM_MODEM_ERROR_GENERAL, - "Could not parse sysinfo results (regex creation failed)."); + g_warning ("Huawei(%s): ^SYSINFO parse regex creation failed.", __func__); goto done; } @@ -207,48 +213,35 @@ sysinfo_done (MMSerialPort *port, /* Say we're registered to something even though sysmode parsing failed */ mm_generic_cdma_query_reg_state_set_callback_1x_state (info, reg_state); } - success = TRUE; - } + } else + g_warning ("Huawei(%s): failed to parse ^SYSINFO response.", __func__); -done: g_match_info_free (match_info); g_regex_unref (r); - - if (!success && !info->error) { - info->error = g_error_new_literal (MM_MODEM_ERROR, - MM_MODEM_ERROR_GENERAL, - "Could not parse sysinfo results."); - } +done: mm_callback_info_schedule (info); } static void query_registration_state (MMGenericCdma *cdma, + MMModemCdmaRegistrationState cur_cdma_state, + MMModemCdmaRegistrationState cur_evdo_state, MMModemCdmaRegistrationStateFn callback, gpointer user_data) { MMCallbackInfo *info; - MMSerialPort *primary, *secondary, *port; - - port = primary = mm_generic_cdma_get_port (cdma, MM_PORT_TYPE_PRIMARY); - secondary = mm_generic_cdma_get_port (cdma, MM_PORT_TYPE_SECONDARY); + MMAtSerialPort *port; - info = mm_generic_cdma_query_reg_state_callback_info_new (cdma, callback, user_data); + info = mm_generic_cdma_query_reg_state_callback_info_new (cdma, cur_cdma_state, cur_evdo_state, callback, user_data); - if (mm_port_get_connected (MM_PORT (primary))) { - if (!secondary) { - info->error = g_error_new_literal (MM_MODEM_ERROR, MM_MODEM_ERROR_CONNECTED, - "Cannot get query registration state while connected"); - mm_callback_info_schedule (info); - return; - } - - /* Use secondary port if primary is connected */ - port = secondary; + port = mm_generic_cdma_get_best_at_port (cdma, &info->error); + if (!port) { + mm_callback_info_schedule (info); + return; } - mm_serial_port_queue_command (port, "^SYSINFO", 3, sysinfo_done, info); + mm_at_serial_port_queue_command (port, "^SYSINFO", 3, sysinfo_done, info); } /*****************************************************************************/ @@ -265,14 +258,14 @@ grab_port (MMModem *modem, GRegex *regex; port = mm_generic_cdma_grab_port (MM_GENERIC_CDMA (modem), subsys, name, suggested_type, user_data, error); - if (port && MM_IS_SERIAL_PORT (port)) { + if (port && MM_IS_AT_SERIAL_PORT (port)) { gboolean evdo0 = FALSE, evdoA = FALSE; g_object_set (G_OBJECT (port), MM_PORT_CARRIER_DETECT, FALSE, NULL); /* 1x signal level */ regex = g_regex_new ("\\r\\n\\^RSSILVL:(\\d+)\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); - mm_serial_port_add_unsolicited_msg_handler (MM_SERIAL_PORT (port), regex, handle_1x_quality_change, modem, NULL); + mm_at_serial_port_add_unsolicited_msg_handler (MM_AT_SERIAL_PORT (port), regex, handle_1x_quality_change, modem, NULL); g_regex_unref (regex); g_object_get (G_OBJECT (modem), @@ -283,7 +276,7 @@ grab_port (MMModem *modem, if (evdo0 || evdoA) { /* EVDO signal level */ regex = g_regex_new ("\\r\\n\\^HRSSILVL:(\\d+)\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); - mm_serial_port_add_unsolicited_msg_handler (MM_SERIAL_PORT (port), regex, handle_evdo_quality_change, modem, NULL); + mm_at_serial_port_add_unsolicited_msg_handler (MM_AT_SERIAL_PORT (port), regex, handle_evdo_quality_change, modem, NULL); g_regex_unref (regex); } } diff --git a/plugins/mm-modem-huawei-gsm.c b/plugins/mm-modem-huawei-gsm.c index d450f25..5123e7f 100644 --- a/plugins/mm-modem-huawei-gsm.c +++ b/plugins/mm-modem-huawei-gsm.c @@ -11,38 +11,40 @@ * GNU General Public License for more details: * * Copyright (C) 2008 - 2009 Novell, Inc. - * Copyright (C) 2009 Red Hat, Inc. + * Copyright (C) 2009 - 2010 Red Hat, Inc. */ #include <stdlib.h> #include <stdio.h> #include <string.h> +#include <errno.h> #define G_UDEV_API_IS_SUBJECT_TO_CHANGE #include <gudev/gudev.h> #include "mm-modem-huawei-gsm.h" #include "mm-modem-gsm-network.h" +#include "mm-modem-gsm-card.h" #include "mm-errors.h" #include "mm-callback-info.h" -#include "mm-serial-port.h" +#include "mm-at-serial-port.h" #include "mm-serial-parsers.h" static void modem_init (MMModem *modem_class); static void modem_gsm_network_init (MMModemGsmNetwork *gsm_network_class); +static void modem_gsm_card_init (MMModemGsmCard *gsm_card_class); G_DEFINE_TYPE_EXTENDED (MMModemHuaweiGsm, mm_modem_huawei_gsm, MM_TYPE_GENERIC_GSM, 0, G_IMPLEMENT_INTERFACE (MM_TYPE_MODEM, modem_init) - G_IMPLEMENT_INTERFACE (MM_TYPE_MODEM_GSM_NETWORK, modem_gsm_network_init)) + G_IMPLEMENT_INTERFACE (MM_TYPE_MODEM_GSM_NETWORK, modem_gsm_network_init) + G_IMPLEMENT_INTERFACE (MM_TYPE_MODEM_GSM_CARD, modem_gsm_card_init)) #define MM_MODEM_HUAWEI_GSM_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), MM_TYPE_MODEM_HUAWEI_GSM, MMModemHuaweiGsmPrivate)) typedef struct { /* Cached state */ - guint signal_quality; - MMModemGsmMode mode; - MMModemGsmBand band; + guint32 band; } MMModemHuaweiGsmPrivate; MMModem * @@ -61,6 +63,61 @@ mm_modem_huawei_gsm_new (const char *device, NULL)); } +/*****************************************************************************/ + +typedef struct { + MMModemGsmBand mm; + guint32 huawei; +} BandTable; + +static BandTable bands[] = { + /* Sort 3G first since it's preferred */ + { MM_MODEM_GSM_BAND_U2100, 0x00400000 }, + { MM_MODEM_GSM_BAND_U1900, 0x00800000 }, + { MM_MODEM_GSM_BAND_U850, 0x04000000 }, + { MM_MODEM_GSM_BAND_U900, 0x00020000 }, + { MM_MODEM_GSM_BAND_G850, 0x00080000 }, + /* 2G second */ + { MM_MODEM_GSM_BAND_DCS, 0x00000080 }, + { MM_MODEM_GSM_BAND_EGSM, 0x00000300 }, /* 0x100 = Extended GSM, 0x200 = Primary GSM */ + { MM_MODEM_GSM_BAND_PCS, 0x00200000 }, + /* And ANY last since it's most inclusive */ + { MM_MODEM_GSM_BAND_ANY, 0x3FFFFFFF }, +}; + +static gboolean +band_mm_to_huawei (MMModemGsmBand band, guint32 *out_huawei) +{ + int i; + + for (i = 0; i < sizeof (bands) / sizeof (BandTable); i++) { + if (bands[i].mm == band) { + *out_huawei = bands[i].huawei; + return TRUE; + } + } + return FALSE; +} + +static gboolean +band_huawei_to_mm (guint32 huawei, MMModemGsmBand *out_mm) +{ + int i; + + for (i = 0; i < sizeof (bands) / sizeof (BandTable); i++) { + /* The dongle returns a bitfield, but since we don't support that + * yet in MM, take the "best" band and return it. + */ + if (bands[i].huawei & huawei) { + *out_mm = bands[i].mm; + return TRUE; + } + } + return FALSE; +} + +/*****************************************************************************/ + static gboolean parse_syscfg (MMModemHuaweiGsm *self, const char *reply, @@ -68,31 +125,31 @@ parse_syscfg (MMModemHuaweiGsm *self, int *mode_b, guint32 *band, int *unknown1, - int *unknown2) + int *unknown2, + MMModemGsmAllowedMode *out_mode) { if (reply == NULL || strncmp (reply, "^SYSCFG:", 8)) return FALSE; if (sscanf (reply + 8, "%d,%d,%x,%d,%d", mode_a, mode_b, band, unknown1, unknown2)) { MMModemHuaweiGsmPrivate *priv = MM_MODEM_HUAWEI_GSM_GET_PRIVATE (self); - + MMModemGsmAllowedMode new_mode = MM_MODEM_GSM_ALLOWED_MODE_ANY; + /* Network mode */ if (*mode_a == 2 && *mode_b == 1) - priv->mode = MM_MODEM_GSM_MODE_2G_PREFERRED; + new_mode = MM_MODEM_GSM_ALLOWED_MODE_2G_PREFERRED; else if (*mode_a == 2 && *mode_b == 2) - priv->mode = MM_MODEM_GSM_MODE_3G_PREFERRED; + new_mode = MM_MODEM_GSM_ALLOWED_MODE_3G_PREFERRED; else if (*mode_a == 13 && *mode_b == 1) - priv->mode = MM_MODEM_GSM_MODE_2G_ONLY; + new_mode = MM_MODEM_GSM_ALLOWED_MODE_2G_ONLY; else if (*mode_a == 14 && *mode_b == 2) - priv->mode = MM_MODEM_GSM_MODE_3G_ONLY; + new_mode = MM_MODEM_GSM_ALLOWED_MODE_3G_ONLY; + + if (out_mode) + *out_mode = new_mode; /* Band */ - if (*band == 0x3FFFFFFF) - priv->band = MM_MODEM_GSM_BAND_ANY; - else if (*band == 0x400380) - priv->band = MM_MODEM_GSM_BAND_DCS; - else if (*band == 0x200000) - priv->band = MM_MODEM_GSM_BAND_PCS; + priv->band = *band; return TRUE; } @@ -101,165 +158,108 @@ parse_syscfg (MMModemHuaweiGsm *self, } static void -set_network_mode_done (MMSerialPort *port, +set_allowed_mode_done (MMAtSerialPort *port, GString *response, GError *error, gpointer user_data) { MMCallbackInfo *info = (MMCallbackInfo *) user_data; - MMModemHuaweiGsm *self = MM_MODEM_HUAWEI_GSM (info->modem); - MMModemHuaweiGsmPrivate *priv = MM_MODEM_HUAWEI_GSM_GET_PRIVATE (self); if (error) info->error = g_error_copy (error); - else - /* Success, cache the value */ - priv->mode = GPOINTER_TO_UINT (mm_callback_info_get_data (info, "mode")); mm_callback_info_schedule (info); } static void -set_network_mode_get_done (MMSerialPort *port, - GString *response, - GError *error, - gpointer user_data) -{ - MMCallbackInfo *info = (MMCallbackInfo *) user_data; - - if (error) { - info->error = g_error_copy (error); - mm_callback_info_schedule (info); - } else { - int a, b, u1, u2; - guint32 band; - - if (parse_syscfg (MM_MODEM_HUAWEI_GSM (info->modem), response->str, &a, &b, &band, &u1, &u2)) { - char *command; - - switch (GPOINTER_TO_UINT (mm_callback_info_get_data (info, "mode"))) { - case MM_MODEM_GSM_MODE_ANY: - a = 2; - b = 0; - break; - case MM_MODEM_GSM_MODE_GPRS: - case MM_MODEM_GSM_MODE_EDGE: - case MM_MODEM_GSM_MODE_2G_ONLY: - a = 13; - b = 1; - break; - case MM_MODEM_GSM_MODE_UMTS: - case MM_MODEM_GSM_MODE_HSDPA: - case MM_MODEM_GSM_MODE_HSUPA: - case MM_MODEM_GSM_MODE_HSPA: - case MM_MODEM_GSM_MODE_3G_ONLY: - a = 14; - b = 2; - break; - case MM_MODEM_GSM_MODE_2G_PREFERRED: - a = 2; - b = 1; - break; - case MM_MODEM_GSM_MODE_3G_PREFERRED: - a = 2; - b = 2; - break; - default: - break; - } - - command = g_strdup_printf ("AT^SYSCFG=%d,%d,%X,%d,%d", a, b, band, u1, u2); - mm_serial_port_queue_command (port, command, 3, set_network_mode_done, info); - g_free (command); - } - } -} - -static void -set_network_mode (MMModemGsmNetwork *modem, - MMModemGsmMode mode, +set_allowed_mode (MMGenericGsm *gsm, + MMModemGsmAllowedMode mode, MMModemFn callback, gpointer user_data) { MMCallbackInfo *info; - MMSerialPort *primary; + MMAtSerialPort *port; + int a, b; + char *command; - info = mm_callback_info_new (MM_MODEM (modem), callback, user_data); + info = mm_callback_info_new (MM_MODEM (gsm), callback, user_data); - switch (mode) { - case MM_MODEM_GSM_MODE_ANY: - case MM_MODEM_GSM_MODE_GPRS: - case MM_MODEM_GSM_MODE_EDGE: - case MM_MODEM_GSM_MODE_UMTS: - case MM_MODEM_GSM_MODE_HSDPA: - case MM_MODEM_GSM_MODE_HSUPA: - case MM_MODEM_GSM_MODE_HSPA: - case MM_MODEM_GSM_MODE_2G_PREFERRED: - case MM_MODEM_GSM_MODE_3G_PREFERRED: - case MM_MODEM_GSM_MODE_2G_ONLY: - case MM_MODEM_GSM_MODE_3G_ONLY: - /* Allowed values */ - mm_callback_info_set_data (info, "mode", GUINT_TO_POINTER (mode), NULL); - primary = mm_generic_gsm_get_port (MM_GENERIC_GSM (modem), MM_PORT_TYPE_PRIMARY); - g_assert (primary); - mm_serial_port_queue_command (primary, "AT^SYSCFG?", 3, set_network_mode_get_done, info); + port = mm_generic_gsm_get_best_at_port (gsm, &info->error); + if (!port) { + mm_callback_info_schedule (info); return; + } + + switch (mode) { + case MM_MODEM_GSM_ALLOWED_MODE_2G_ONLY: + a = 13; + b = 1; + break; + case MM_MODEM_GSM_ALLOWED_MODE_3G_ONLY: + a = 14; + b = 2; + break; + case MM_MODEM_GSM_ALLOWED_MODE_2G_PREFERRED: + a = 2; + b = 1; + break; + case MM_MODEM_GSM_ALLOWED_MODE_3G_PREFERRED: + a = 2; + b = 2; + break; + case MM_MODEM_GSM_ALLOWED_MODE_ANY: default: - info->error = g_error_new_literal (MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL, "Invalid mode."); + a = 2; + b = 0; break; } - mm_callback_info_schedule (info); + command = g_strdup_printf ("AT^SYSCFG=%d,%d,40000000,2,4", a, b); + mm_at_serial_port_queue_command (port, command, 3, set_allowed_mode_done, info); + g_free (command); } static void -get_network_mode_done (MMSerialPort *port, +get_allowed_mode_done (MMAtSerialPort *port, GString *response, GError *error, gpointer user_data) { MMCallbackInfo *info = (MMCallbackInfo *) user_data; MMModemHuaweiGsm *self = MM_MODEM_HUAWEI_GSM (info->modem); - MMModemHuaweiGsmPrivate *priv = MM_MODEM_HUAWEI_GSM_GET_PRIVATE (self); int mode_a, mode_b, u1, u2; guint32 band; + MMModemGsmAllowedMode mode = MM_MODEM_GSM_ALLOWED_MODE_ANY; if (error) info->error = g_error_copy (error); - else if (parse_syscfg (self, response->str, &mode_a, &mode_b, &band, &u1, &u2)) - mm_callback_info_set_result (info, GUINT_TO_POINTER (priv->mode), NULL); + else if (parse_syscfg (self, response->str, &mode_a, &mode_b, &band, &u1, &u2, &mode)) + mm_callback_info_set_result (info, GUINT_TO_POINTER (mode), NULL); mm_callback_info_schedule (info); } static void -get_network_mode (MMModemGsmNetwork *modem, +get_allowed_mode (MMGenericGsm *gsm, MMModemUIntFn callback, gpointer user_data) { - MMModemHuaweiGsmPrivate *priv = MM_MODEM_HUAWEI_GSM_GET_PRIVATE (modem); + MMCallbackInfo *info; + MMAtSerialPort *port; - if (priv->mode != MM_MODEM_GSM_MODE_ANY) { - /* have cached mode (from an unsolicited message). Use that */ - MMCallbackInfo *info; + info = mm_callback_info_uint_new (MM_MODEM (gsm), callback, user_data); - info = mm_callback_info_uint_new (MM_MODEM (modem), callback, user_data); - mm_callback_info_set_result (info, GUINT_TO_POINTER (priv->mode), NULL); + port = mm_generic_gsm_get_best_at_port (gsm, &info->error); + if (!port) { mm_callback_info_schedule (info); - } else { - /* Get it from modem */ - MMCallbackInfo *info; - MMSerialPort *primary; - - info = mm_callback_info_uint_new (MM_MODEM (modem), callback, user_data); - primary = mm_generic_gsm_get_port (MM_GENERIC_GSM (modem), MM_PORT_TYPE_PRIMARY); - g_assert (primary); - mm_serial_port_queue_command (primary, "AT^SYSCFG?", 3, get_network_mode_done, info); + return; } + + mm_at_serial_port_queue_command (port, "AT^SYSCFG?", 3, get_allowed_mode_done, info); } static void -set_band_done (MMSerialPort *port, +set_band_done (MMAtSerialPort *port, GString *response, GError *error, gpointer user_data) @@ -270,108 +270,63 @@ set_band_done (MMSerialPort *port, if (error) info->error = g_error_copy (error); - else + else { /* Success, cache the value */ priv->band = GPOINTER_TO_UINT (mm_callback_info_get_data (info, "band")); + } mm_callback_info_schedule (info); } static void -set_band_get_done (MMSerialPort *port, - GString *response, - GError *error, - gpointer user_data) -{ - MMCallbackInfo *info = (MMCallbackInfo *) user_data; - MMModemHuaweiGsm *self = MM_MODEM_HUAWEI_GSM (info->modem); - - if (error) { - info->error = g_error_copy (error); - mm_callback_info_schedule (info); - } else { - int a, b, u1, u2; - guint32 band; - - if (parse_syscfg (self, response->str, &a, &b, &band, &u1, &u2)) { - char *command; - - switch (GPOINTER_TO_UINT (mm_callback_info_get_data (info, "band"))) { - case MM_MODEM_GSM_BAND_ANY: - band = 0x3FFFFFFF; - break; - case MM_MODEM_GSM_BAND_EGSM: - band = 0x100; - break; - case MM_MODEM_GSM_BAND_DCS: - band = 0x80; - break; - case MM_MODEM_GSM_BAND_U2100: - band = 0x400000; - break; - case MM_MODEM_GSM_BAND_PCS: - band = 0x200000; - break; - case MM_MODEM_GSM_BAND_G850: - band = 0x80000; - break; - default: - break; - } - - command = g_strdup_printf ("AT^SYSCFG=%d,%d,%X,%d,%d", a, b, band, u1, u2); - mm_serial_port_queue_command (port, command, 3, set_band_done, info); - g_free (command); - } - } -} - -static void set_band (MMModemGsmNetwork *modem, MMModemGsmBand band, MMModemFn callback, gpointer user_data) { MMCallbackInfo *info; - MMSerialPort *primary; + MMAtSerialPort *port; + char *command; + guint32 huawei_band = 0x3FFFFFFF; info = mm_callback_info_new (MM_MODEM (modem), callback, user_data); - switch (band) { - case MM_MODEM_GSM_BAND_ANY: - case MM_MODEM_GSM_BAND_EGSM: - case MM_MODEM_GSM_BAND_DCS: - case MM_MODEM_GSM_BAND_U2100: - case MM_MODEM_GSM_BAND_PCS: - mm_callback_info_set_data (info, "band", GUINT_TO_POINTER (band), NULL); - primary = mm_generic_gsm_get_port (MM_GENERIC_GSM (modem), MM_PORT_TYPE_PRIMARY); - g_assert (primary); - mm_serial_port_queue_command (primary, "AT^SYSCFG?", 3, set_band_get_done, info); + port = mm_generic_gsm_get_best_at_port (MM_GENERIC_GSM (modem), &info->error); + if (!port) { + mm_callback_info_schedule (info); return; - default: - info->error = g_error_new_literal (MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL, "Invalid band."); - break; } - mm_callback_info_schedule (info); + if (!band_mm_to_huawei (band, &huawei_band)) { + info->error = g_error_new_literal (MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL, "Invalid band."); + mm_callback_info_schedule (info); + } else { + mm_callback_info_set_data (info, "band", GUINT_TO_POINTER (huawei_band), NULL); + command = g_strdup_printf ("AT^SYSCFG=16,3,%X,2,4", huawei_band); + mm_at_serial_port_queue_command (port, command, 3, set_band_done, info); + g_free (command); + } } static void -get_band_done (MMSerialPort *port, +get_band_done (MMAtSerialPort *port, GString *response, GError *error, gpointer user_data) { MMCallbackInfo *info = (MMCallbackInfo *) user_data; MMModemHuaweiGsm *self = MM_MODEM_HUAWEI_GSM (info->modem); - MMModemHuaweiGsmPrivate *priv = MM_MODEM_HUAWEI_GSM_GET_PRIVATE (self); int mode_a, mode_b, u1, u2; guint32 band; if (error) info->error = g_error_copy (error); - else if (parse_syscfg (self, response->str, &mode_a, &mode_b, &band, &u1, &u2)) - mm_callback_info_set_result (info, GUINT_TO_POINTER (priv->band), NULL); + else if (parse_syscfg (self, response->str, &mode_a, &mode_b, &band, &u1, &u2, NULL)) { + MMModemGsmBand mm_band = MM_MODEM_GSM_BAND_ANY; + + band_huawei_to_mm (band, &mm_band); + mm_callback_info_set_result (info, GUINT_TO_POINTER (mm_band), NULL); + } mm_callback_info_schedule (info); } @@ -382,84 +337,289 @@ get_band (MMModemGsmNetwork *modem, gpointer user_data) { MMModemHuaweiGsmPrivate *priv = MM_MODEM_HUAWEI_GSM_GET_PRIVATE (modem); - MMSerialPort *primary; + MMAtSerialPort *port; + MMCallbackInfo *info; + + info = mm_callback_info_uint_new (MM_MODEM (modem), callback, user_data); - if (priv->band != MM_MODEM_GSM_BAND_ANY) { - /* have cached mode (from an unsolicited message). Use that */ - MMCallbackInfo *info; + /* Prefer cached band from unsolicited messages if we have it */ + if (priv->band != 0) { + MMModemGsmBand mm_band = MM_MODEM_GSM_BAND_ANY; - info = mm_callback_info_uint_new (MM_MODEM (modem), callback, user_data); - mm_callback_info_set_result (info, GUINT_TO_POINTER (priv->band), NULL); + band_huawei_to_mm (priv->band, &mm_band); + mm_callback_info_set_result (info, GUINT_TO_POINTER (mm_band), NULL); mm_callback_info_schedule (info); - } else { - /* Get it from modem */ - MMCallbackInfo *info; + return; + } - info = mm_callback_info_uint_new (MM_MODEM (modem), callback, user_data); - primary = mm_generic_gsm_get_port (MM_GENERIC_GSM (modem), MM_PORT_TYPE_PRIMARY); - g_assert (primary); - mm_serial_port_queue_command (primary, "AT^SYSCFG?", 3, get_band_done, info); + /* Otherwise ask the modem */ + port = mm_generic_gsm_get_best_at_port (MM_GENERIC_GSM (modem), &info->error); + if (!port) { + mm_callback_info_schedule (info); + return; } + + mm_at_serial_port_queue_command (port, "AT^SYSCFG?", 3, get_band_done, info); } static void -get_signal_quality (MMModemGsmNetwork *modem, +get_act_request_done (MMAtSerialPort *port, + GString *response, + GError *error, + gpointer user_data) +{ + MMCallbackInfo *info = user_data; + MMModemGsmAccessTech act = MM_MODEM_GSM_ACCESS_TECH_UNKNOWN; + GRegex *r = NULL; + GMatchInfo *match_info = NULL; + char *str; + int srv_stat = 0; + + if (error) { + info->error = g_error_copy (error); + goto done; + } + + /* Can't just use \d here since sometimes you get "^SYSINFO:2,1,0,3,1,,3" */ + r = g_regex_new ("\\^SYSINFO:\\s*(\\d?),(\\d?),(\\d?),(\\d?),(\\d?),(\\d?),(\\d?)$", G_REGEX_UNGREEDY, 0, NULL); + if (!r) { + g_set_error_literal (&info->error, + MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL, + "Could not parse ^SYSINFO results."); + goto done; + } + + if (!g_regex_match_full (r, response->str, response->len, 0, 0, &match_info, &info->error)) { + g_set_error_literal (&info->error, + MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL, + "Could not parse ^SYSINFO results."); + goto done; + } + + str = g_match_info_fetch (match_info, 1); + if (str && strlen (str)) + srv_stat = atoi (str); + g_free (str); + + if (srv_stat != 0) { + /* Valid service */ + str = g_match_info_fetch (match_info, 7); + if (str && strlen (str)) { + if (str[0] == '1') + act = MM_MODEM_GSM_ACCESS_TECH_GSM; + else if (str[0] == '2') + act = MM_MODEM_GSM_ACCESS_TECH_GPRS; + else if (str[0] == '3') + act = MM_MODEM_GSM_ACCESS_TECH_EDGE; + else if (str[0] == '4') + act = MM_MODEM_GSM_ACCESS_TECH_UMTS; + else if (str[0] == '5') + act = MM_MODEM_GSM_ACCESS_TECH_HSDPA; + else if (str[0] == '6') + act = MM_MODEM_GSM_ACCESS_TECH_HSUPA; + else if (str[0] == '7') + act = MM_MODEM_GSM_ACCESS_TECH_HSPA; + } + g_free (str); + } + +done: + if (match_info) + g_match_info_free (match_info); + if (r) + g_regex_unref (r); + mm_callback_info_set_result (info, GUINT_TO_POINTER (act), NULL); + mm_callback_info_schedule (info); +} + +static void +get_access_technology (MMGenericGsm *modem, + MMModemUIntFn callback, + gpointer user_data) +{ + MMAtSerialPort *port; + MMCallbackInfo *info; + + info = mm_callback_info_uint_new (MM_MODEM (modem), callback, user_data); + + port = mm_generic_gsm_get_best_at_port (modem, &info->error); + if (!port) { + mm_callback_info_schedule (info); + return; + } + + mm_at_serial_port_queue_command (port, "^SYSINFO", 3, get_act_request_done, info); +} + +/*****************************************************************************/ + +static gboolean +parse_num (const char *str, guint32 *out_num, guint32 min, guint32 max) +{ + unsigned long int tmp; + + if (!str || !strlen (str)) + return FALSE; + + errno = 0; + tmp = strtoul (str, NULL, 10); + if (errno != 0 || tmp < min || tmp > max) + return FALSE; + *out_num = (guint32) tmp; + return TRUE; +} + +static void +send_huawei_cpin_done (MMAtSerialPort *port, + GString *response, + GError *error, + gpointer user_data) +{ + MMCallbackInfo *info = (MMCallbackInfo *) user_data; + GRegex *r = NULL; + GMatchInfo *match_info = NULL; + const char *pin_type; + guint32 attempts_left = 0; + char *str = NULL; + guint32 num = 0; + + if (error) { + info->error = g_error_copy (error); + goto done; + } + + pin_type = mm_callback_info_get_data (info, "pin_type"); + + r = g_regex_new ("\\^CPIN:\\s*([^,]+),[^,]*,(\\d+),(\\d+),(\\d+),(\\d+)", G_REGEX_UNGREEDY, 0, NULL); + if (!r) { + g_set_error_literal (&info->error, + MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL, + "Could not parse ^CPIN results (error creating regex)."); + goto done; + } + + if (!g_regex_match_full (r, response->str, response->len, 0, 0, &match_info, &info->error)) { + g_set_error_literal (&info->error, + MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL, + "Could not parse ^CPIN results (match failed)."); + goto done; + } + + if (strstr (pin_type, MM_MODEM_GSM_CARD_SIM_PUK)) + num = 2; + else if (strstr (pin_type, MM_MODEM_GSM_CARD_SIM_PIN)) + num = 3; + else if (strstr (pin_type, MM_MODEM_GSM_CARD_SIM_PUK2)) + num = 4; + else if (strstr (pin_type, MM_MODEM_GSM_CARD_SIM_PIN2)) + num = 5; + else { + g_debug ("%s: unhandled pin type '%s'", __func__, pin_type); + + info->error = g_error_new_literal (MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL, "Unhandled PIN type"); + } + + if (num > 0) { + gboolean success = FALSE; + + str = g_match_info_fetch (match_info, num); + if (str) { + success = parse_num (str, &attempts_left, 0, 10); + g_free (str); + } + + if (!success) { + info->error = g_error_new_literal (MM_MODEM_ERROR, + MM_MODEM_ERROR_GENERAL, + "Could not parse ^CPIN results (missing or invalid match info)."); + } + } + + mm_callback_info_set_result (info, GUINT_TO_POINTER (attempts_left), NULL); + + g_match_info_free (match_info); + +done: + if (r) + g_regex_unref (r); + mm_serial_port_close (MM_SERIAL_PORT (port)); + mm_callback_info_schedule (info); +} + +static void +get_unlock_retries (MMModemGsmCard *modem, + const char *pin_type, MMModemUIntFn callback, gpointer user_data) { - MMModemHuaweiGsmPrivate *priv = MM_MODEM_HUAWEI_GSM_GET_PRIVATE (modem); + MMAtSerialPort *port; + char *command; + MMCallbackInfo *info = mm_callback_info_uint_new (MM_MODEM (modem), callback, user_data); - if (priv->signal_quality) { - /* have cached signal quality (from an unsolicited message). Use that */ - MMCallbackInfo *info; + g_debug ("%s: pin type '%s'", __func__, pin_type); - info = mm_callback_info_uint_new (MM_MODEM (modem), callback, user_data); - mm_callback_info_set_result (info, GUINT_TO_POINTER (priv->signal_quality), NULL); + /* Ensure we have a usable port to use for the command */ + port = mm_generic_gsm_get_best_at_port (MM_GENERIC_GSM (modem), &info->error); + if (!port) { mm_callback_info_schedule (info); - } else { - /* Use the generic implementation */ - MMModemGsmNetwork *parent_gsm_network_iface; + return; + } - parent_gsm_network_iface = g_type_interface_peek_parent (MM_MODEM_GSM_NETWORK_GET_INTERFACE (modem)); - parent_gsm_network_iface->get_signal_quality (modem, callback, user_data); + /* Modem may not be enabled yet, which sometimes can't be done until + * the device has been unlocked. In this case we have to open the port + * ourselves. + */ + if (!mm_serial_port_open (MM_SERIAL_PORT (port), &info->error)) { + mm_callback_info_schedule (info); + return; } + + /* if the modem have not yet been enabled we need to make sure echoing is turned off */ + command = g_strdup_printf ("E0"); + mm_at_serial_port_queue_command (port, command, 3, NULL, NULL); + g_free (command); + + mm_callback_info_set_data (info, "pin_type", g_strdup (pin_type), g_free); + + command = g_strdup_printf ("^CPIN?"); + mm_at_serial_port_queue_command (port, command, 3, send_huawei_cpin_done, info); + g_free (command); } +/*****************************************************************************/ /* Unsolicited message handlers */ static void -handle_signal_quality_change (MMSerialPort *port, +handle_signal_quality_change (MMAtSerialPort *port, GMatchInfo *match_info, gpointer user_data) { MMModemHuaweiGsm *self = MM_MODEM_HUAWEI_GSM (user_data); - MMModemHuaweiGsmPrivate *priv = MM_MODEM_HUAWEI_GSM_GET_PRIVATE (self); char *str; - int quality; + int quality = 0; str = g_match_info_fetch (match_info, 1); quality = atoi (str); g_free (str); - if (quality == 99) + if (quality == 99) { /* 99 means unknown */ quality = 0; - else + } else { /* Normalize the quality */ - quality = quality * 100 / 31; + quality = CLAMP (quality, 0, 31) * 100 / 31; + } - g_debug ("Signal quality: %d", quality); - priv->signal_quality = (guint32) quality; - mm_modem_gsm_network_signal_quality (MM_MODEM_GSM_NETWORK (self), (guint32) quality); + mm_generic_gsm_update_signal_quality (MM_GENERIC_GSM (self), (guint32) quality); } static void -handle_mode_change (MMSerialPort *port, +handle_mode_change (MMAtSerialPort *port, GMatchInfo *match_info, gpointer user_data) { MMModemHuaweiGsm *self = MM_MODEM_HUAWEI_GSM (user_data); - MMModemHuaweiGsmPrivate *priv = MM_MODEM_HUAWEI_GSM_GET_PRIVATE (self); + MMModemGsmAccessTech act = MM_MODEM_GSM_ACCESS_TECH_UNKNOWN; char *str; int a; int b; @@ -472,29 +632,35 @@ handle_mode_change (MMSerialPort *port, b = atoi (str); g_free (str); - if (a == 3 && b == 2) - priv->mode = MM_MODEM_GSM_MODE_GPRS; - else if (a == 3 && b == 3) - priv->mode = MM_MODEM_GSM_MODE_EDGE; - else if (a == 5 && b == 4) - priv->mode = MM_MODEM_GSM_MODE_UMTS; - else if (a == 5 && b == 5) - priv->mode = MM_MODEM_GSM_MODE_HSDPA; - else if (a == 5 && b == 6) - priv->mode = MM_MODEM_GSM_MODE_HSUPA; - else if (a == 5 && b == 7) - priv->mode = MM_MODEM_GSM_MODE_HSPA; + if (a == 3) { /* GSM/GPRS mode */ + if (b == 1) + act = MM_MODEM_GSM_ACCESS_TECH_GSM; + else if (b == 2) + act = MM_MODEM_GSM_ACCESS_TECH_GPRS; + else if (b == 3) + act = MM_MODEM_GSM_ACCESS_TECH_EDGE; + } else if (a == 5) { /* WCDMA mode */ + if (b == 4) + act = MM_MODEM_GSM_ACCESS_TECH_UMTS; + else if (b == 5) + act = MM_MODEM_GSM_ACCESS_TECH_HSDPA; + else if (b == 6) + act = MM_MODEM_GSM_ACCESS_TECH_HSUPA; + else if (b == 7) + act = MM_MODEM_GSM_ACCESS_TECH_HSPA; + } else if (a == 0) + act = MM_MODEM_GSM_ACCESS_TECH_UNKNOWN; else { g_warning ("Couldn't parse mode change value: '%s'", str); return; } - g_debug ("Mode: %d", priv->mode); - mm_modem_gsm_network_mode (MM_MODEM_GSM_NETWORK (self), priv->mode); + g_debug ("Access Technology: %d", act); + mm_generic_gsm_update_access_technology (MM_GENERIC_GSM (self), act); } static void -handle_status_change (MMSerialPort *port, +handle_status_change (MMAtSerialPort *port, GMatchInfo *match_info, gpointer user_data) { @@ -546,38 +712,35 @@ grab_port (MMModem *modem, } if (usbif == 0) { - if (!mm_generic_gsm_get_port (gsm, MM_PORT_TYPE_PRIMARY)) + if (!mm_generic_gsm_get_at_port (gsm, MM_PORT_TYPE_PRIMARY)) ptype = MM_PORT_TYPE_PRIMARY; } else if (suggested_type == MM_PORT_TYPE_SECONDARY) { - if (!mm_generic_gsm_get_port (gsm, MM_PORT_TYPE_SECONDARY)) + if (!mm_generic_gsm_get_at_port (gsm, MM_PORT_TYPE_SECONDARY)) ptype = MM_PORT_TYPE_SECONDARY; } port = mm_generic_gsm_grab_port (gsm, subsys, name, ptype, error); - if (port && MM_IS_SERIAL_PORT (port)) { - g_object_set (G_OBJECT (port), MM_PORT_CARRIER_DETECT, FALSE, NULL); - if (ptype == MM_PORT_TYPE_SECONDARY) { - GRegex *regex; + if (port && MM_IS_AT_SERIAL_PORT (port)) { + GRegex *regex; - mm_generic_gsm_set_unsolicited_registration (MM_GENERIC_GSM (modem), TRUE); + g_object_set (G_OBJECT (port), MM_PORT_CARRIER_DETECT, FALSE, NULL); - regex = g_regex_new ("\\r\\n\\^RSSI:(\\d+)\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); - mm_serial_port_add_unsolicited_msg_handler (MM_SERIAL_PORT (port), regex, handle_signal_quality_change, modem, NULL); - g_regex_unref (regex); + regex = g_regex_new ("\\r\\n\\^RSSI:(\\d+)\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); + mm_at_serial_port_add_unsolicited_msg_handler (MM_AT_SERIAL_PORT (port), regex, handle_signal_quality_change, modem, NULL); + g_regex_unref (regex); - regex = g_regex_new ("\\r\\n\\^MODE:(\\d),(\\d)\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); - mm_serial_port_add_unsolicited_msg_handler (MM_SERIAL_PORT (port), regex, handle_mode_change, modem, NULL); - g_regex_unref (regex); + regex = g_regex_new ("\\r\\n\\^MODE:(\\d),(\\d)\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); + mm_at_serial_port_add_unsolicited_msg_handler (MM_AT_SERIAL_PORT (port), regex, handle_mode_change, modem, NULL); + g_regex_unref (regex); - regex = g_regex_new ("\\r\\n\\^DSFLOWRPT:(.+)\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); - mm_serial_port_add_unsolicited_msg_handler (MM_SERIAL_PORT (port), regex, handle_status_change, modem, NULL); - g_regex_unref (regex); + regex = g_regex_new ("\\r\\n\\^DSFLOWRPT:(.+)\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); + mm_at_serial_port_add_unsolicited_msg_handler (MM_AT_SERIAL_PORT (port), regex, handle_status_change, modem, NULL); + g_regex_unref (regex); - regex = g_regex_new ("\\r\\n\\^BOOT:.+\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); - mm_serial_port_add_unsolicited_msg_handler (MM_SERIAL_PORT (port), regex, NULL, modem, NULL); - g_regex_unref (regex); - } + regex = g_regex_new ("\\r\\n\\^BOOT:.+\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); + mm_at_serial_port_add_unsolicited_msg_handler (MM_AT_SERIAL_PORT (port), regex, NULL, modem, NULL); + g_regex_unref (regex); } out: @@ -598,11 +761,14 @@ modem_init (MMModem *modem_class) static void modem_gsm_network_init (MMModemGsmNetwork *class) { - class->set_network_mode = set_network_mode; - class->get_network_mode = get_network_mode; class->set_band = set_band; class->get_band = get_band; - class->get_signal_quality = get_signal_quality; +} + +static void +modem_gsm_card_init (MMModemGsmCard *class) +{ + class->get_unlock_retries = get_unlock_retries; } static void @@ -614,8 +780,13 @@ static void mm_modem_huawei_gsm_class_init (MMModemHuaweiGsmClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); + MMGenericGsmClass *gsm_class = MM_GENERIC_GSM_CLASS (klass); mm_modem_huawei_gsm_parent_class = g_type_class_peek_parent (klass); g_type_class_add_private (object_class, sizeof (MMModemHuaweiGsmPrivate)); + + gsm_class->set_allowed_mode = set_allowed_mode; + gsm_class->get_allowed_mode = get_allowed_mode; + gsm_class->get_access_technology = get_access_technology; } diff --git a/plugins/mm-modem-longcheer-gsm.c b/plugins/mm-modem-longcheer-gsm.c new file mode 100644 index 0000000..62980f7 --- /dev/null +++ b/plugins/mm-modem-longcheer-gsm.c @@ -0,0 +1,222 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details: + * + * Copyright (C) 2008 - 2009 Novell, Inc. + * Copyright (C) 2009 - 2010 Red Hat, Inc. + */ + +#include <config.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> +#include "mm-modem-longcheer-gsm.h" +#include "mm-at-serial-port.h" +#include "mm-errors.h" +#include "mm-callback-info.h" +#include "mm-modem-helpers.h" + +G_DEFINE_TYPE (MMModemLongcheerGsm, mm_modem_longcheer_gsm, MM_TYPE_GENERIC_GSM) + +MMModem * +mm_modem_longcheer_gsm_new (const char *device, + const char *driver, + const char *plugin) +{ + g_return_val_if_fail (device != NULL, NULL); + g_return_val_if_fail (driver != NULL, NULL); + g_return_val_if_fail (plugin != NULL, NULL); + + return MM_MODEM (g_object_new (MM_TYPE_MODEM_LONGCHEER_GSM, + MM_MODEM_MASTER_DEVICE, device, + MM_MODEM_DRIVER, driver, + MM_MODEM_PLUGIN, plugin, + NULL)); +} + +/*****************************************************************************/ + +static void +get_allowed_mode_done (MMAtSerialPort *port, + GString *response, + GError *error, + gpointer user_data) +{ + MMCallbackInfo *info = (MMCallbackInfo *) user_data; + const char *p; + MMModemGsmAllowedMode mode = MM_MODEM_GSM_ALLOWED_MODE_ANY; + gint mododr = -1; + + info->error = mm_modem_check_removed (info->modem, error); + if (info->error) + goto done; + + p = mm_strip_tag (response->str, "+MODODR:"); + if (!p) { + info->error = g_error_new_literal (MM_MODEM_ERROR, + MM_MODEM_ERROR_GENERAL, + "Failed to parse the allowed mode response"); + goto done; + } + + mododr = atoi (p); + switch (mododr) { + case 1: + mode = MM_MODEM_GSM_ALLOWED_MODE_3G_ONLY; + break; + case 2: + case 4: + mode = MM_MODEM_GSM_ALLOWED_MODE_3G_PREFERRED; + break; + case 3: + mode = MM_MODEM_GSM_ALLOWED_MODE_2G_ONLY; + break; + default: + break; + } + mm_callback_info_set_result (info, GUINT_TO_POINTER (mode), NULL); + +done: + mm_callback_info_schedule (info); +} + +static void +get_allowed_mode (MMGenericGsm *gsm, + MMModemUIntFn callback, + gpointer user_data) +{ + MMCallbackInfo *info; + MMAtSerialPort *port; + + info = mm_callback_info_uint_new (MM_MODEM (gsm), callback, user_data); + + port = mm_generic_gsm_get_best_at_port (gsm, &info->error); + if (!port) { + mm_callback_info_schedule (info); + return; + } + + mm_at_serial_port_queue_command (port, "AT+MODODR?", 3, get_allowed_mode_done, info); +} + +static void +set_allowed_mode_done (MMAtSerialPort *port, + GString *response, + GError *error, + gpointer user_data) +{ + MMCallbackInfo *info = (MMCallbackInfo *) user_data; + + if (error) + info->error = g_error_copy (error); + + mm_callback_info_schedule (info); +} + +static void +set_allowed_mode (MMGenericGsm *gsm, + MMModemGsmAllowedMode mode, + MMModemFn callback, + gpointer user_data) +{ + MMCallbackInfo *info; + MMAtSerialPort *port; + char *command; + int mododr = 0; + + info = mm_callback_info_new (MM_MODEM (gsm), callback, user_data); + + port = mm_generic_gsm_get_best_at_port (gsm, &info->error); + if (!port) { + mm_callback_info_schedule (info); + return; + } + + switch (mode) { + case MM_MODEM_GSM_ALLOWED_MODE_2G_ONLY: + mododr = 3; + break; + case MM_MODEM_GSM_ALLOWED_MODE_3G_ONLY: + mododr = 1; + break; + case MM_MODEM_GSM_ALLOWED_MODE_ANY: + case MM_MODEM_GSM_ALLOWED_MODE_3G_PREFERRED: + case MM_MODEM_GSM_ALLOWED_MODE_2G_PREFERRED: + default: + mododr = 2; + break; + } + + command = g_strdup_printf ("+MODODR=%d", mododr); + mm_at_serial_port_queue_command (port, command, 3, set_allowed_mode_done, info); + g_free (command); +} + +static void +get_act_request_done (MMAtSerialPort *port, + GString *response, + GError *error, + gpointer user_data) +{ + MMCallbackInfo *info = user_data; + MMModemGsmAccessTech act = MM_MODEM_GSM_ACCESS_TECH_UNKNOWN; + const char *p; + + if (error) + info->error = g_error_copy (error); + else { + p = mm_strip_tag (response->str, "+PSRAT:"); + act = mm_gsm_string_to_access_tech (p); + } + + mm_callback_info_set_result (info, GUINT_TO_POINTER (act), NULL); + mm_callback_info_schedule (info); +} + +static void +get_access_technology (MMGenericGsm *modem, + MMModemUIntFn callback, + gpointer user_data) +{ + MMAtSerialPort *port; + MMCallbackInfo *info; + + info = mm_callback_info_uint_new (MM_MODEM (modem), callback, user_data); + + port = mm_generic_gsm_get_best_at_port (modem, &info->error); + if (!port) { + mm_callback_info_schedule (info); + return; + } + + mm_at_serial_port_queue_command (port, "+PSRAT", 3, get_act_request_done, info); +} + +/*****************************************************************************/ + +static void +mm_modem_longcheer_gsm_init (MMModemLongcheerGsm *self) +{ +} + +static void +mm_modem_longcheer_gsm_class_init (MMModemLongcheerGsmClass *klass) +{ + MMGenericGsmClass *gsm_class = MM_GENERIC_GSM_CLASS (klass); + + mm_modem_longcheer_gsm_parent_class = g_type_class_peek_parent (klass); + + gsm_class->set_allowed_mode = set_allowed_mode; + gsm_class->get_allowed_mode = get_allowed_mode; + gsm_class->get_access_technology = get_access_technology; +} + diff --git a/plugins/mm-modem-longcheer-gsm.h b/plugins/mm-modem-longcheer-gsm.h new file mode 100644 index 0000000..5383c52 --- /dev/null +++ b/plugins/mm-modem-longcheer-gsm.h @@ -0,0 +1,43 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details: + * + * Copyright (C) 2008 - 2009 Novell, Inc. + * Copyright (C) 2009 Red Hat, Inc. + */ + +#ifndef MM_MODEM_LONGCHEER_GSM_H +#define MM_MODEM_LONGCHEER_GSM_H + +#include "mm-generic-gsm.h" + +#define MM_TYPE_MODEM_LONGCHEER_GSM (mm_modem_longcheer_gsm_get_type ()) +#define MM_MODEM_LONGCHEER_GSM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_MODEM_LONGCHEER_GSM, MMModemLongcheerGsm)) +#define MM_MODEM_LONGCHEER_GSM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_MODEM_LONGCHEER_GSM, MMModemLongcheerGsmClass)) +#define MM_IS_MODEM_LONGCHEER_GSM(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_MODEM_LONGCHEER_GSM)) +#define MM_IS_MODEM_LONGCHEER_GSM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_MODEM_LONGCHEER_GSM)) +#define MM_MODEM_LONGCHEER_GSM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_MODEM_LONGCHEER_GSM, MMModemLongcheerGsmClass)) + +typedef struct { + MMGenericGsm parent; +} MMModemLongcheerGsm; + +typedef struct { + MMGenericGsmClass parent; +} MMModemLongcheerGsmClass; + +GType mm_modem_longcheer_gsm_get_type (void); + +MMModem *mm_modem_longcheer_gsm_new (const char *device, + const char *driver, + const char *plugin); + +#endif /* MM_MODEM_LONGCHEER_H */ diff --git a/plugins/mm-modem-mbm.c b/plugins/mm-modem-mbm.c index 686b35c..7f6bc9c 100644 --- a/plugins/mm-modem-mbm.c +++ b/plugins/mm-modem-mbm.c @@ -1,11 +1,13 @@ /* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* - * Copyright (C) 2008 Ericsson AB + * Copyright (C) 2008 - 2010 Ericsson AB + * Copyright (C) 2009 - 2010 Red Hat, Inc. * * Author: Per Hallsmark <per.hallsmark@ericsson.com> * Bjorn Runaker <bjorn.runaker@ericsson.com> * Torgny Johansson <torgny.johansson@ericsson.com> * Jonas Sjöquist <jonas.sjoquist@ericsson.com> + * Dan Williams <dcbw@redhat.com> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -17,10 +19,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - * */ #include <stdlib.h> @@ -30,17 +28,20 @@ #include "mm-modem-mbm.h" #include "mm-modem-simple.h" +#include "mm-modem-gsm-card.h" #include "mm-errors.h" #include "mm-callback-info.h" static void modem_init (MMModem *modem_class); static void modem_gsm_network_init (MMModemGsmNetwork *gsm_network_class); static void modem_simple_init (MMModemSimple *class); +static void modem_gsm_card_init (MMModemGsmCard *class); G_DEFINE_TYPE_EXTENDED (MMModemMbm, mm_modem_mbm, MM_TYPE_GENERIC_GSM, 0, G_IMPLEMENT_INTERFACE (MM_TYPE_MODEM, modem_init) G_IMPLEMENT_INTERFACE (MM_TYPE_MODEM_GSM_NETWORK, modem_gsm_network_init) - G_IMPLEMENT_INTERFACE (MM_TYPE_MODEM_SIMPLE, modem_simple_init)) + G_IMPLEMENT_INTERFACE (MM_TYPE_MODEM_SIMPLE, modem_simple_init) + G_IMPLEMENT_INTERFACE (MM_TYPE_MODEM_GSM_CARD, modem_gsm_card_init)) #define MM_MODEM_MBM_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), MM_TYPE_MODEM_MBM, MMModemMbmPrivate)) @@ -54,11 +55,6 @@ G_DEFINE_TYPE_EXTENDED (MMModemMbm, mm_modem_mbm, MM_TYPE_GENERIC_GSM, 0, #define MBM_NETWORK_MODE_2G 5 #define MBM_NETWORK_MODE_3G 6 -#define MBM_ERINFO_2G_GPRS 1 -#define MBM_ERINFO_2G_EGPRS 2 -#define MBM_ERINFO_3G_UMTS 1 -#define MBM_ERINFO_3G_HSDPA 2 - typedef struct { guint reg_id; gboolean have_emrdy; @@ -83,12 +79,6 @@ mbm_modem_authenticate (MMModemMbm *self, const char *password, gpointer user_data); -static const char * -mbm_simple_get_string_property (GHashTable *properties, const char *name, GError **error); - -static uint -mbm_simple_get_uint_property (GHashTable *properties, const char *name, GError **error); - MMModem * mm_modem_mbm_new (const char *device, const char *driver, @@ -124,16 +114,9 @@ register_done (gpointer user_data) MMModemMbm *self = MM_MODEM_MBM (reg_data->modem); MMModemMbmPrivate *priv = MM_MODEM_MBM_GET_PRIVATE (self); MMModemGsmNetwork *parent_modem_iface; - MMSerialPort *primary; priv->reg_id = 0; - primary = mm_generic_gsm_get_port (MM_GENERIC_GSM (self), MM_PORT_TYPE_PRIMARY); - g_assert (primary); - - mm_serial_port_queue_command (primary, "+CREG=1", 3, NULL, NULL); - mm_serial_port_queue_command (primary, "+CMER=3,0,0,1", 3, NULL, NULL); - parent_modem_iface = g_type_interface_peek_parent (MM_MODEM_GSM_NETWORK_GET_INTERFACE (self)); parent_modem_iface->do_register (MM_MODEM_GSM_NETWORK (self), reg_data->network_id, @@ -167,21 +150,16 @@ do_register (MMModemGsmNetwork *modem, } static int -mbm_parse_network_mode (MMModemGsmMode network_mode) +mbm_parse_allowed_mode (MMModemGsmAllowedMode network_mode) { switch (network_mode) { - case MM_MODEM_GSM_MODE_ANY: - case MM_MODEM_GSM_MODE_3G_PREFERRED: - case MM_MODEM_GSM_MODE_2G_PREFERRED: + case MM_MODEM_GSM_ALLOWED_MODE_ANY: + case MM_MODEM_GSM_ALLOWED_MODE_3G_PREFERRED: + case MM_MODEM_GSM_ALLOWED_MODE_2G_PREFERRED: return MBM_NETWORK_MODE_ANY; - case MM_MODEM_GSM_MODE_GPRS: - case MM_MODEM_GSM_MODE_EDGE: - case MM_MODEM_GSM_MODE_2G_ONLY: + case MM_MODEM_GSM_ALLOWED_MODE_2G_ONLY: return MBM_NETWORK_MODE_2G; - case MM_MODEM_GSM_MODE_3G_ONLY: - case MM_MODEM_GSM_MODE_HSDPA: - case MM_MODEM_GSM_MODE_HSUPA: - case MM_MODEM_GSM_MODE_HSPA: + case MM_MODEM_GSM_ALLOWED_MODE_3G_ONLY: return MBM_NETWORK_MODE_3G; default: return MBM_NETWORK_MODE_ANY; @@ -189,7 +167,7 @@ mbm_parse_network_mode (MMModemGsmMode network_mode) } static void -mbm_set_network_mode_done (MMSerialPort *port, +mbm_set_allowed_mode_done (MMAtSerialPort *port, GString *response, GError *error, gpointer user_data) @@ -203,92 +181,144 @@ mbm_set_network_mode_done (MMSerialPort *port, } static void -set_network_mode (MMModemGsmNetwork *modem, - MMModemGsmMode mode, +set_allowed_mode (MMGenericGsm *gsm, + MMModemGsmAllowedMode mode, MMModemFn callback, gpointer user_data) { MMCallbackInfo *info; char *command; - MMSerialPort *primary; + MMAtSerialPort *port; - info = mm_callback_info_new (MM_MODEM (modem), callback, user_data); - primary = mm_generic_gsm_get_port (MM_GENERIC_GSM (modem), MM_PORT_TYPE_PRIMARY); - g_assert (primary); + info = mm_callback_info_new (MM_MODEM (gsm), callback, user_data); + + port = mm_generic_gsm_get_best_at_port (gsm, &info->error); + if (!port) { + mm_callback_info_schedule (info); + return; + } - command = g_strdup_printf ("+CFUN=%d", mbm_parse_network_mode (mode)); - mm_serial_port_queue_command (primary, command, 3, mbm_set_network_mode_done, info); + command = g_strdup_printf ("+CFUN=%d", mbm_parse_allowed_mode (mode)); + mm_at_serial_port_queue_command (port, command, 3, mbm_set_allowed_mode_done, info); g_free (command); } static void -get_network_mode_done (MMSerialPort *port, +mbm_erinfo_received (MMAtSerialPort *port, + GMatchInfo *info, + gpointer user_data) +{ + MMModemGsmAccessTech act = MM_MODEM_GSM_ACCESS_TECH_UNKNOWN; + char *str; + + str = g_match_info_fetch (info, 2); + if (str) { + switch (atoi (str)) { + case 1: + act = MM_MODEM_GSM_ACCESS_TECH_GPRS; + break; + case 2: + act = MM_MODEM_GSM_ACCESS_TECH_EDGE; + break; + default: + break; + } + } + g_free (str); + + /* 3G modes take precedence */ + str = g_match_info_fetch (info, 3); + if (str) { + switch (atoi (str)) { + case 1: + act = MM_MODEM_GSM_ACCESS_TECH_UMTS; + break; + case 2: + act = MM_MODEM_GSM_ACCESS_TECH_HSDPA; + break; + default: + break; + } + } + g_free (str); + + mm_generic_gsm_update_access_technology (MM_GENERIC_GSM (user_data), act); +} + +static void +get_allowed_mode_done (MMAtSerialPort *port, GString *response, GError *error, gpointer user_data) { MMCallbackInfo *info = (MMCallbackInfo *) user_data; - char *erinfo; - int mode = 0, gsm = 0, umts = 0; gboolean parsed = FALSE; - if (error) { + if (error) info->error = g_error_copy (error); - goto done; - } - - erinfo = strstr (response->str, "*ERINFO:"); - if (!erinfo) - goto done; - - if (sscanf (erinfo + 8, "%d,%d,%d", &mode, &gsm, &umts) != 3) - goto done; + else if (!g_str_has_prefix (response->str, "CFUN: ")) { + MMModemGsmAllowedMode mode = MM_MODEM_GSM_ALLOWED_MODE_ANY; + int a; - if (gsm || umts) { - MMModemGsmMode mm_mode = MM_MODEM_GSM_MODE_ANY; - - if (gsm == MBM_ERINFO_2G_GPRS) - mm_mode = MM_MODEM_GSM_MODE_GPRS; - else if (gsm == MBM_ERINFO_2G_EGPRS) - mm_mode = MM_MODEM_GSM_MODE_EDGE; - else if (umts == MBM_ERINFO_3G_UMTS) - mm_mode = MM_MODEM_GSM_MODE_UMTS; - else if (umts == MBM_ERINFO_3G_HSDPA) - mm_mode = MM_MODEM_GSM_MODE_HSDPA; - else - g_debug ("%s unknown network mode %d,%d", __FUNCTION__, gsm, umts); + a = atoi (response->str + 6); + if (a == MBM_NETWORK_MODE_2G) + mode = MM_MODEM_GSM_ALLOWED_MODE_2G_ONLY; + else if (a == MBM_NETWORK_MODE_3G) + mode = MM_MODEM_GSM_ALLOWED_MODE_3G_ONLY; - mm_callback_info_set_result (info, GUINT_TO_POINTER (mm_mode), NULL); + mm_callback_info_set_result (info, GUINT_TO_POINTER (mode), NULL); parsed = TRUE; } -done: - if (!error && !parsed) { + if (!error && !parsed) info->error = g_error_new_literal (MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL, - "Could not parse network mode results"); - } + "Could not parse allowed mode results"); mm_callback_info_schedule (info); } static void -get_network_mode (MMModemGsmNetwork *modem, +get_allowed_mode (MMGenericGsm *gsm, MMModemUIntFn callback, gpointer user_data) { MMCallbackInfo *info; - MMSerialPort *primary; + MMAtSerialPort *port; - info = mm_callback_info_uint_new (MM_MODEM (modem), callback, user_data); - primary = mm_generic_gsm_get_port (MM_GENERIC_GSM (modem), MM_PORT_TYPE_PRIMARY); - g_assert (primary); - mm_serial_port_queue_command (primary, "*ERINFO?", 3, get_network_mode_done, info); + info = mm_callback_info_uint_new (MM_MODEM (gsm), callback, user_data); + + port = mm_generic_gsm_get_best_at_port (gsm, &info->error); + if (!port) { + mm_callback_info_schedule (info); + return; + } + + mm_at_serial_port_queue_command (port, "+CFUN?", 3, get_allowed_mode_done, info); } /*****************************************************************************/ /* Simple Modem class override functions */ /*****************************************************************************/ +static const char * +mbm_simple_get_string_property (GHashTable *properties, const char *name, GError **error) +{ + GValue *value; + + value = (GValue *) g_hash_table_lookup (properties, name); + if (!value) + return NULL; + + if (G_VALUE_HOLDS_STRING (value)) + return g_value_get_string (value); + + g_set_error (error, MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL, + "Invalid property type for '%s': %s (string expected)", + name, G_VALUE_TYPE_NAME (value)); + + return NULL; +} + static void simple_connect (MMModemSimple *simple, GHashTable *properties, @@ -298,14 +328,10 @@ simple_connect (MMModemSimple *simple, MMModemMbmPrivate *priv = MM_MODEM_MBM_GET_PRIVATE (simple); MMCallbackInfo *info = (MMCallbackInfo *) user_data; MMModemSimple *parent_iface; - uint network_mode = 0; priv->username = mbm_simple_get_string_property (properties, "username", &info->error); priv->password = mbm_simple_get_string_property (properties, "password", &info->error); - network_mode = mbm_simple_get_uint_property (properties, "network_mode", &info->error); - priv->network_mode = mbm_parse_network_mode (network_mode); - parent_iface = g_type_interface_peek_parent (MM_MODEM_SIMPLE_GET_INTERFACE (simple)); parent_iface->connect (MM_MODEM_SIMPLE (simple), properties, callback, info); } @@ -315,18 +341,22 @@ simple_connect (MMModemSimple *simple, /*****************************************************************************/ static void -mbm_enable_done (MMSerialPort *port, +mbm_enable_done (MMAtSerialPort *port, GString *response, GError *error, gpointer user_data) { MMCallbackInfo *info = (MMCallbackInfo *) user_data; + /* Start unsolicited signal strength and access technology responses */ + mm_at_serial_port_queue_command (port, "+CMER=3,0,0,1", 3, NULL, NULL); + mm_at_serial_port_queue_command (port, "*ERINFO=1", 3, NULL, NULL); + mm_generic_gsm_enable_complete (MM_GENERIC_GSM (info->modem), error, info); } static void -mbm_enap0_done (MMSerialPort *port, +mbm_enap0_done (MMAtSerialPort *port, GString *response, GError *error, gpointer user_data) @@ -339,12 +369,12 @@ mbm_enap0_done (MMSerialPort *port, priv->network_mode = MBM_NETWORK_MODE_ANY; command = g_strdup_printf ("+CFUN=%d", priv->network_mode); - mm_serial_port_queue_command (port, command, 3, mbm_enable_done, info); + mm_at_serial_port_queue_command (port, command, 3, mbm_enable_done, info); g_free (command); } static void -mbm_init_done (MMSerialPort *port, +mbm_init_done (MMAtSerialPort *port, GString *response, GError *error, gpointer user_data) @@ -360,17 +390,17 @@ mbm_init_done (MMSerialPort *port, if (!priv->network_mode) priv->network_mode = MBM_NETWORK_MODE_ANY; - mm_serial_port_queue_command (port, "*ENAP=0", 3, mbm_enap0_done, info); + mm_at_serial_port_queue_command (port, "*ENAP=0", 3, mbm_enap0_done, info); } static void -do_init (MMSerialPort *port, MMCallbackInfo *info) +do_init (MMAtSerialPort *port, MMCallbackInfo *info) { - mm_serial_port_queue_command (port, "&F E0 V1 X4 &C1 +CMEE=1", 3, mbm_init_done, info); + mm_at_serial_port_queue_command (port, "&F E0 V1 X4 &C1 +CMEE=1", 3, mbm_init_done, info); } static void -mbm_emrdy_done (MMSerialPort *port, +mbm_emrdy_done (MMAtSerialPort *port, GString *response, GError *error, gpointer user_data) @@ -378,11 +408,9 @@ mbm_emrdy_done (MMSerialPort *port, MMCallbackInfo *info = user_data; MMModemMbmPrivate *priv = MM_MODEM_MBM_GET_PRIVATE (info->modem); - if ( error - && error->domain == MM_SERIAL_ERROR - && error->code == MM_SERIAL_RESPONSE_TIMEOUT) { + if (g_error_matches (error, MM_SERIAL_ERROR, MM_SERIAL_ERROR_RESPONSE_TIMEOUT)) g_warning ("%s: timed out waiting for EMRDY response.", __func__); - } else + else priv->have_emrdy = TRUE; do_init (port, info); @@ -393,18 +421,18 @@ do_enable (MMGenericGsm *self, MMModemFn callback, gpointer user_data) { MMModemMbmPrivate *priv = MM_MODEM_MBM_GET_PRIVATE (self); MMCallbackInfo *info; - MMSerialPort *primary; + MMAtSerialPort *primary; info = mm_callback_info_new (MM_MODEM (self), callback, user_data); - primary = mm_generic_gsm_get_port (self, MM_PORT_TYPE_PRIMARY); + primary = mm_generic_gsm_get_at_port (self, MM_PORT_TYPE_PRIMARY); g_assert (primary); if (priv->have_emrdy) { /* Modem is ready, no need to check EMRDY */ do_init (primary, info); } else - mm_serial_port_queue_command (primary, "*EMRDY?", 5, mbm_emrdy_done, info); + mm_at_serial_port_queue_command (primary, "*EMRDY?", 5, mbm_emrdy_done, info); } typedef struct { @@ -414,10 +442,10 @@ typedef struct { } DisableInfo; static void -disable_creg_cmer_done (MMSerialPort *port, - GString *response, - GError *error, - gpointer user_data) +disable_unsolicited_done (MMAtSerialPort *port, + GString *response, + GError *error, + gpointer user_data) { MMModem *parent_modem_iface; @@ -433,7 +461,7 @@ disable (MMModem *modem, MMModemFn callback, gpointer user_data) { - MMSerialPort *primary; + MMAtSerialPort *primary; DisableInfo *info; info = g_malloc0 (sizeof (DisableInfo)); @@ -441,11 +469,11 @@ disable (MMModem *modem, info->user_data = user_data; info->modem = modem; - primary = mm_generic_gsm_get_port (MM_GENERIC_GSM (modem), MM_PORT_TYPE_PRIMARY); + primary = mm_generic_gsm_get_at_port (MM_GENERIC_GSM (modem), MM_PORT_TYPE_PRIMARY); g_assert (primary); - /* Turn off unsolicited +CIEV signal strength indicator */ - mm_serial_port_queue_command (primary, "+CREG=0;+CMER=0", 5, disable_creg_cmer_done, info); + /* Turn off unsolicited responses */ + mm_at_serial_port_queue_command (primary, "+CMER=0;*ERINFO=0", 5, disable_unsolicited_done, info); } static void @@ -466,29 +494,77 @@ do_connect (MMModem *modem, } static void -disconnect (MMModem *modem, - MMModemFn callback, - gpointer user_data) +do_disconnect (MMGenericGsm *gsm, + gint cid, + MMModemFn callback, + gpointer user_data) { - MMCallbackInfo *info; - MMSerialPort *primary; - - mm_modem_set_state (modem, MM_MODEM_STATE_DISCONNECTING, MM_MODEM_STATE_REASON_NONE); + MMAtSerialPort *primary; - primary = mm_generic_gsm_get_port (MM_GENERIC_GSM (modem), MM_PORT_TYPE_PRIMARY); + primary = mm_generic_gsm_get_at_port (gsm, MM_PORT_TYPE_PRIMARY); g_assert (primary); - mm_serial_port_queue_command (primary, "*ENAP=0", 3, NULL, NULL); + mm_at_serial_port_queue_command (primary, "*ENAP=0", 3, NULL, NULL); - mm_generic_gsm_update_enabled_state (MM_GENERIC_GSM (modem), FALSE, MM_MODEM_STATE_REASON_NONE); + MM_GENERIC_GSM_CLASS (mm_modem_mbm_parent_class)->do_disconnect (gsm, cid, callback, user_data); +} - info = mm_callback_info_new (modem, callback, user_data); +static void +factory_reset_done (MMAtSerialPort *port, + GString *response, + GError *error, + gpointer user_data) +{ + MMCallbackInfo *info = (MMCallbackInfo *) user_data; + + mm_serial_port_close (MM_SERIAL_PORT (port)); mm_callback_info_schedule (info); } +static void +factory_reset (MMModem *self, + const char *code, + MMModemFn callback, + gpointer user_data) +{ + MMAtSerialPort *port; + MMCallbackInfo *info; + + info = mm_callback_info_new (self, callback, user_data); + + /* Ensure we have a usable port to use for the command */ + port = mm_generic_gsm_get_best_at_port (MM_GENERIC_GSM (self), &info->error); + if (!port) { + mm_callback_info_schedule (info); + return; + } + + /* Modem may not be enabled yet, which sometimes can't be done until + * the device has been unlocked. In this case we have to open the port + * ourselves. + */ + if (!mm_serial_port_open (MM_SERIAL_PORT (port), &info->error)) { + mm_callback_info_schedule (info); + return; + } + + mm_at_serial_port_queue_command (port, "&F +CMEE=0", 3, NULL, NULL); + mm_at_serial_port_queue_command (port, "+COPS=0", 3, NULL, NULL); + mm_at_serial_port_queue_command (port, "+CR=0", 3, NULL, NULL); + mm_at_serial_port_queue_command (port, "+CRC=0", 3, NULL, NULL); + mm_at_serial_port_queue_command (port, "+CREG=0", 3, NULL, NULL); + mm_at_serial_port_queue_command (port, "+CMER=0", 3, NULL, NULL); + mm_at_serial_port_queue_command (port, "*EPEE=0", 3, NULL, NULL); + mm_at_serial_port_queue_command (port, "+CNMI=2, 0, 0, 0, 0", 3, NULL, NULL); + mm_at_serial_port_queue_command (port, "+CGREG=0", 3, NULL, NULL); + mm_at_serial_port_queue_command (port, "*EIAD=0", 3, NULL, NULL); + mm_at_serial_port_queue_command (port, "+CGSMS=3", 3, NULL, NULL); + mm_at_serial_port_queue_command (port, "+CSCA=\"\",129", 3, factory_reset_done, info); +} + /*****************************************************************************/ static void -mbm_emrdy_received (MMSerialPort *port, +mbm_emrdy_received (MMAtSerialPort *port, GMatchInfo *info, gpointer user_data) { @@ -496,7 +572,7 @@ mbm_emrdy_received (MMSerialPort *port, } static void -mbm_pacsp_received (MMSerialPort *port, +mbm_pacsp_received (MMAtSerialPort *port, GMatchInfo *info, gpointer user_data) { @@ -504,65 +580,78 @@ mbm_pacsp_received (MMSerialPort *port, } static void -mbm_ciev_received (MMSerialPort *port, +mbm_ciev_received (MMAtSerialPort *port, GMatchInfo *info, gpointer user_data) { int quality = 0, ind = 0; - const char *str; + char *str; str = g_match_info_fetch (info, 1); if (str) ind = atoi (str); + g_free (str); if (ind == MBM_SIGNAL_INDICATOR) { str = g_match_info_fetch (info, 2); if (str) { quality = atoi (str); - mm_modem_gsm_network_signal_quality (MM_MODEM_GSM_NETWORK (user_data), quality * 20); + mm_generic_gsm_update_signal_quality (MM_GENERIC_GSM (user_data), quality * 20); } + g_free (str); } } static void -mbm_do_connect_done (MMModemMbm *self) +mbm_do_connect_done (MMModemMbm *self, gboolean success) { MMModemMbmPrivate *priv = MM_MODEM_MBM_GET_PRIVATE (self); - if (priv->pending_connect_info) { + if (!priv->pending_connect_info) + return; + + if (success) mm_generic_gsm_connect_complete (MM_GENERIC_GSM (self), NULL, priv->pending_connect_info); - priv->pending_connect_info = NULL; + else { + GError *connect_error; + + connect_error = mm_modem_connect_error_for_code (MM_MODEM_CONNECT_ERROR_BUSY); + mm_generic_gsm_connect_complete (MM_GENERIC_GSM (self), connect_error, priv->pending_connect_info); + g_error_free (connect_error); } + priv->pending_connect_info = NULL; } static void -mbm_e2nap_received (MMSerialPort *port, +mbm_e2nap_received (MMAtSerialPort *port, GMatchInfo *info, gpointer user_data) { int state = 0; - const char *str; + char *str; str = g_match_info_fetch (info, 1); if (str) state = atoi (str); - - if (MBM_E2NAP_DISCONNECTED == state) - g_debug ("%s, disconnected", __func__); - else if (MBM_E2NAP_CONNECTED == state) { - g_debug ("%s, connected", __func__); - mbm_do_connect_done (MM_MODEM_MBM (user_data)); + g_free (str); + + if (MBM_E2NAP_DISCONNECTED == state) { + g_debug ("%s: disconnected", __func__); + mbm_do_connect_done (MM_MODEM_MBM (user_data), FALSE); + } else if (MBM_E2NAP_CONNECTED == state) { + g_debug ("%s: connected", __func__); + mbm_do_connect_done (MM_MODEM_MBM (user_data), TRUE); } else if (MBM_E2NAP_CONNECTING == state) - g_debug("%s, connecting", __func__); + g_debug("%s: connecting", __func__); else { /* Should not happen */ - g_debug("%s, undefined e2nap status",__FUNCTION__); - g_assert_not_reached (); + g_debug("%s: unhandled E2NAP state %d", __func__, state); + mbm_do_connect_done (MM_MODEM_MBM (user_data), FALSE); } } static void -enap_poll_response (MMSerialPort *port, +enap_poll_response (MMAtSerialPort *port, GString *response, GError *error, gpointer user_data) @@ -597,20 +686,21 @@ static gboolean enap_poll (gpointer user_data) { MMCallbackInfo *info = (MMCallbackInfo *) user_data; - MMSerialPort *port = mm_generic_gsm_get_port (MM_GENERIC_GSM (info->modem), MM_PORT_TYPE_PRIMARY); + MMAtSerialPort *port; + port = mm_generic_gsm_get_at_port (MM_GENERIC_GSM (info->modem), MM_PORT_TYPE_PRIMARY); g_assert (port); - mm_serial_port_queue_command (port, "AT*ENAP?", 3, enap_poll_response, user_data); + mm_at_serial_port_queue_command (port, "AT*ENAP?", 3, enap_poll_response, user_data); /* we cancel this in the _done function if all is fine */ return TRUE; } static void -enap_done (MMSerialPort *port, - GString *response, - GError *error, - gpointer user_data) +enap_done (MMAtSerialPort *port, + GString *response, + GError *error, + gpointer user_data) { MMCallbackInfo *info = (MMCallbackInfo *) user_data; guint tid; @@ -623,12 +713,11 @@ enap_done (MMSerialPort *port, tid = g_timeout_add_seconds (1, enap_poll, user_data); /* remember poll id as callback info object, with source_remove as free func */ mm_callback_info_set_data (info, "mbm-enap-poll-id", GUINT_TO_POINTER (tid), (GFreeFunc) g_source_remove); - mm_serial_port_queue_command (port, "AT*E2NAP=1", 3, NULL, NULL); } static void mbm_auth_done (MMSerialPort *port, - GString *response, + GByteArray *response, GError *error, gpointer user_data) { @@ -643,8 +732,11 @@ mbm_auth_done (MMSerialPort *port, } cid = mm_generic_gsm_get_cid (modem); + + mm_at_serial_port_queue_command (MM_AT_SERIAL_PORT (port), "AT*E2NAP=1", 3, NULL, NULL); + command = g_strdup_printf ("AT*ENAP=1,%d", cid); - mm_serial_port_queue_command (port, command, 3, enap_done, user_data); + mm_at_serial_port_queue_command (MM_AT_SERIAL_PORT (port), command, 3, enap_done, user_data); g_free (command); } @@ -654,61 +746,133 @@ mbm_modem_authenticate (MMModemMbm *self, const char *password, gpointer user_data) { - MMSerialPort *primary; + MMAtSerialPort *primary; - primary = mm_generic_gsm_get_port (MM_GENERIC_GSM (self), MM_PORT_TYPE_PRIMARY); + primary = mm_generic_gsm_get_at_port (MM_GENERIC_GSM (self), MM_PORT_TYPE_PRIMARY); g_assert (primary); if (username || password) { - char *command; + GByteArray *command; + MMModemCharset cur_set; + char *tmp; + + /* F3507g at least wants the username and password to be sent in the + * modem's current character set. + */ + cur_set = mm_generic_gsm_get_charset (MM_GENERIC_GSM (self)); + + command = g_byte_array_sized_new (75); + tmp = g_strdup_printf ("AT*EIAAUW=%d,1,", mm_generic_gsm_get_cid (MM_GENERIC_GSM (self))); + g_byte_array_append (command, (const guint8 *) tmp, strlen (tmp)); + g_free (tmp); + + if (username) + mm_modem_charset_byte_array_append (command, username, TRUE, cur_set); + else + g_byte_array_append (command, (const guint8 *) "\"\"", 2); - command = g_strdup_printf ("*EIAAUW=%d,1,\"%s\",\"%s\"", - mm_generic_gsm_get_cid (MM_GENERIC_GSM (self)), - username ? username : "", - password ? password : ""); + g_byte_array_append (command, (const guint8 *) ",", 1); - mm_serial_port_queue_command (primary, command, 3, mbm_auth_done, user_data); - g_free (command); + if (password) + mm_modem_charset_byte_array_append (command, password, TRUE, cur_set); + else + g_byte_array_append (command, (const guint8 *) "\"\"", 2); + + g_byte_array_append (command, (const guint8 *) "\r", 1); + + mm_serial_port_queue_command (MM_SERIAL_PORT (primary), + command, + TRUE, + 3, + mbm_auth_done, + user_data); } else - mbm_auth_done (primary, NULL, NULL, user_data); + mbm_auth_done (MM_SERIAL_PORT (primary), NULL, NULL, user_data); } -static const char * -mbm_simple_get_string_property (GHashTable *properties, const char *name, GError **error) +/*****************************************************************************/ + +static void +send_epin_done (MMAtSerialPort *port, + GString *response, + GError *error, + gpointer user_data) { - GValue *value; + MMCallbackInfo *info = (MMCallbackInfo *) user_data; + const char *pin_type; + int attempts_left = 0; - value = (GValue *) g_hash_table_lookup (properties, name); - if (!value) - return NULL; + if (error) { + info->error = g_error_copy (error); + goto done; + } - if (G_VALUE_HOLDS_STRING (value)) - return g_value_get_string (value); + pin_type = mm_callback_info_get_data (info, "pin_type"); - g_set_error (error, MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL, - "Invalid property type for '%s': %s (string expected)", - name, G_VALUE_TYPE_NAME (value)); + if (strstr (pin_type, MM_MODEM_GSM_CARD_SIM_PIN)) + sscanf (response->str, "*EPIN: %d", &attempts_left); + else if (strstr (pin_type, MM_MODEM_GSM_CARD_SIM_PUK)) + sscanf (response->str, "*EPIN: %*d, %d", &attempts_left); + else if (strstr (pin_type, MM_MODEM_GSM_CARD_SIM_PIN2)) + sscanf (response->str, "*EPIN: %*d, %*d, %d", &attempts_left); + else if (strstr (pin_type, MM_MODEM_GSM_CARD_SIM_PUK2)) + sscanf (response->str, "*EPIN: %*d, %*d, %*d, %d", &attempts_left); + else { + g_debug ("%s: unhandled pin type '%s'", __func__, pin_type); - return NULL; + info->error = g_error_new_literal (MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL, "Unhandled PIN type"); + } + + if (attempts_left < 0 || attempts_left > 998) { + info->error = g_error_new (MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL, "Invalid PIN attempts left %d", attempts_left); + attempts_left = 0; + } + + mm_callback_info_set_result (info, GUINT_TO_POINTER (attempts_left), NULL); + +done: + mm_serial_port_close (MM_SERIAL_PORT (port)); + mm_callback_info_schedule (info); } -static uint -mbm_simple_get_uint_property (GHashTable *properties, const char *name, GError **error) +static void +mbm_get_unlock_retries (MMModemGsmCard *modem, + const char *pin_type, + MMModemUIntFn callback, + gpointer user_data) { - GValue *value; + MMAtSerialPort *port; + char *command; + MMCallbackInfo *info = mm_callback_info_uint_new (MM_MODEM (modem), callback, user_data); - value = (GValue *) g_hash_table_lookup (properties, name); - if (!value) - return 0; + g_debug ("%s: pin type '%s'", __func__, pin_type); - if (G_VALUE_HOLDS_UINT (value)) - return g_value_get_uint (value); + /* Ensure we have a usable port to use for the command */ + port = mm_generic_gsm_get_best_at_port (MM_GENERIC_GSM (modem), &info->error); + if (!port) { + mm_callback_info_schedule (info); + return; + } - g_set_error (error, MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL, - "Invalid property type for '%s': %s (uint expected)", - name, G_VALUE_TYPE_NAME (value)); + /* Modem may not be enabled yet, which sometimes can't be done until + * the device has been unlocked. In this case we have to open the port + * ourselves. + */ + if (!mm_serial_port_open (MM_SERIAL_PORT (port), &info->error)) { + mm_callback_info_schedule (info); + return; + } + + /* if the modem have not yet been enabled we need to make sure echoing is turned off */ + command = g_strdup_printf ("E0"); + mm_at_serial_port_queue_command (port, command, 3, NULL, NULL); + g_free (command); - return 0; + mm_callback_info_set_data (info, "pin_type", g_strdup (pin_type), g_free); + + command = g_strdup_printf ("*EPIN?"); + mm_at_serial_port_queue_command (port, command, 3, send_epin_done, info); + g_free (command); } /*****************************************************************************/ @@ -727,43 +891,52 @@ grab_port (MMModem *modem, if (!strcmp (subsys, "tty")) { if (suggested_type == MM_PORT_TYPE_UNKNOWN) { - if (!mm_generic_gsm_get_port (gsm, MM_PORT_TYPE_PRIMARY)) + if (!mm_generic_gsm_get_at_port (gsm, MM_PORT_TYPE_PRIMARY)) ptype = MM_PORT_TYPE_PRIMARY; - else if (!mm_generic_gsm_get_port (gsm, MM_PORT_TYPE_SECONDARY)) + else if (!mm_generic_gsm_get_at_port (gsm, MM_PORT_TYPE_SECONDARY)) ptype = MM_PORT_TYPE_SECONDARY; } else ptype = suggested_type; } port = mm_generic_gsm_grab_port (gsm, subsys, name, ptype, error); - if (port && MM_IS_SERIAL_PORT (port) && (ptype == MM_PORT_TYPE_PRIMARY)) { + if (port && MM_IS_AT_SERIAL_PORT (port)) { GRegex *regex; - mm_generic_gsm_set_unsolicited_registration (MM_GENERIC_GSM (modem), TRUE); + if (ptype == MM_PORT_TYPE_PRIMARY) { + regex = g_regex_new ("\\r\\n\\*E2NAP: (\\d)\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); + mm_at_serial_port_add_unsolicited_msg_handler (MM_AT_SERIAL_PORT (port), regex, mbm_e2nap_received, modem, NULL); + g_regex_unref (regex); - regex = g_regex_new ("\\r\\n\\*EMRDY: \\d\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); - mm_serial_port_add_unsolicited_msg_handler (MM_SERIAL_PORT (port), regex, mbm_emrdy_received, modem, NULL); - g_regex_unref (regex); + /* Catch the extended error status bit of the command too */ + regex = g_regex_new ("\\r\\n\\*E2NAP: (\\d),.*\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); + mm_at_serial_port_add_unsolicited_msg_handler (MM_AT_SERIAL_PORT (port), regex, mbm_e2nap_received, modem, NULL); + g_regex_unref (regex); + } - regex = g_regex_new ("\\r\\n\\*E2NAP: (\\d)\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); - mm_serial_port_add_unsolicited_msg_handler (MM_SERIAL_PORT (port), regex, mbm_e2nap_received, modem, NULL); + regex = g_regex_new ("\\r\\n\\*EMRDY: \\d\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); + mm_at_serial_port_add_unsolicited_msg_handler (MM_AT_SERIAL_PORT (port), regex, mbm_emrdy_received, modem, NULL); g_regex_unref (regex); regex = g_regex_new ("\\r\\n\\+PACSP(\\d)\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); - mm_serial_port_add_unsolicited_msg_handler (MM_SERIAL_PORT (port), regex, mbm_pacsp_received, modem, NULL); + mm_at_serial_port_add_unsolicited_msg_handler (MM_AT_SERIAL_PORT (port), regex, mbm_pacsp_received, modem, NULL); g_regex_unref (regex); regex = g_regex_new ("\\r\\n\\+CIEV: (\\d),(\\d)\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); - mm_serial_port_add_unsolicited_msg_handler (MM_SERIAL_PORT (port), regex, mbm_ciev_received, modem, NULL); + mm_at_serial_port_add_unsolicited_msg_handler (MM_AT_SERIAL_PORT (port), regex, mbm_ciev_received, modem, NULL); g_regex_unref (regex); /* also consume unsolicited mbm messages we are not interested in them - see LP: #416418 */ - regex = g_regex_new ("\\R\\*ESTKSMENU:.*\\R", G_REGEX_RAW | G_REGEX_OPTIMIZE | G_REGEX_MULTILINE | G_REGEX_NEWLINE_CRLF, G_REGEX_MATCH_NEWLINE_CRLF, NULL);
- mm_serial_port_add_unsolicited_msg_handler (MM_SERIAL_PORT (port), regex, NULL, modem, NULL); + regex = g_regex_new ("\\R\\*ESTKSMENU:.*\\R", G_REGEX_RAW | G_REGEX_OPTIMIZE | G_REGEX_MULTILINE | G_REGEX_NEWLINE_CRLF, G_REGEX_MATCH_NEWLINE_CRLF, NULL); + mm_at_serial_port_add_unsolicited_msg_handler (MM_AT_SERIAL_PORT (port), regex, NULL, NULL, NULL); g_regex_unref (regex); regex = g_regex_new ("\\r\\n\\*EMWI: (\\d),(\\d).*\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); - mm_serial_port_add_unsolicited_msg_handler (MM_SERIAL_PORT (port), regex, NULL, NULL, NULL); + mm_at_serial_port_add_unsolicited_msg_handler (MM_AT_SERIAL_PORT (port), regex, NULL, NULL, NULL); + g_regex_unref (regex); + + regex = g_regex_new ("\\r\\n\\*ERINFO:\\s*(\\d),(\\d),(\\d).*\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); + mm_at_serial_port_add_unsolicited_msg_handler (MM_AT_SERIAL_PORT (port), regex, mbm_erinfo_received, modem, NULL); g_regex_unref (regex); } @@ -773,11 +946,15 @@ grab_port (MMModem *modem, /*****************************************************************************/ static void +modem_gsm_card_init (MMModemGsmCard *class) +{ + class->get_unlock_retries = mbm_get_unlock_retries; +} + +static void modem_gsm_network_init (MMModemGsmNetwork *class) { class->do_register = do_register; - class->get_network_mode = get_network_mode; - class->set_network_mode = set_network_mode; } static void @@ -792,7 +969,7 @@ modem_init (MMModem *modem_class) modem_class->grab_port = grab_port; modem_class->disable = disable; modem_class->connect = do_connect; - modem_class->disconnect = disconnect; + modem_class->factory_reset = factory_reset; } static void @@ -826,5 +1003,8 @@ mm_modem_mbm_class_init (MMModemMbmClass *klass) object_class->finalize = finalize; gsm_class->do_enable = do_enable; + gsm_class->do_disconnect = do_disconnect; + gsm_class->get_allowed_mode = get_allowed_mode; + gsm_class->set_allowed_mode = set_allowed_mode; } diff --git a/plugins/mm-modem-mbm.h b/plugins/mm-modem-mbm.h index 8756e47..db0f627 100644 --- a/plugins/mm-modem-mbm.h +++ b/plugins/mm-modem-mbm.h @@ -17,10 +17,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - * */ #ifndef MM_MODEM_MBM_H diff --git a/plugins/mm-modem-nokia.c b/plugins/mm-modem-nokia.c index 677a089..eb90287 100644 --- a/plugins/mm-modem-nokia.c +++ b/plugins/mm-modem-nokia.c @@ -56,19 +56,19 @@ grab_port (MMModem *modem, MMPort *port = NULL; if (suggested_type == MM_PORT_TYPE_UNKNOWN) { - if (!mm_generic_gsm_get_port (gsm, MM_PORT_TYPE_PRIMARY)) + if (!mm_generic_gsm_get_at_port (gsm, MM_PORT_TYPE_PRIMARY)) ptype = MM_PORT_TYPE_PRIMARY; - else if (!mm_generic_gsm_get_port (gsm, MM_PORT_TYPE_SECONDARY)) + else if (!mm_generic_gsm_get_at_port (gsm, MM_PORT_TYPE_SECONDARY)) ptype = MM_PORT_TYPE_SECONDARY; } else ptype = suggested_type; port = mm_generic_gsm_grab_port (gsm, subsys, name, ptype, error); - if (port && MM_IS_SERIAL_PORT (port)) { - mm_serial_port_set_response_parser (MM_SERIAL_PORT (port), - mm_serial_parser_v1_e1_parse, - mm_serial_parser_v1_e1_new (), - mm_serial_parser_v1_e1_destroy); + if (port && MM_IS_AT_SERIAL_PORT (port)) { + mm_at_serial_port_set_response_parser (MM_AT_SERIAL_PORT (port), + mm_serial_parser_v1_e1_parse, + mm_serial_parser_v1_e1_new (), + mm_serial_parser_v1_e1_destroy); } return !!port; diff --git a/plugins/mm-modem-novatel-cdma.c b/plugins/mm-modem-novatel-cdma.c new file mode 100644 index 0000000..64ee15f --- /dev/null +++ b/plugins/mm-modem-novatel-cdma.c @@ -0,0 +1,183 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details: + * + * Copyright (C) 2008 - 2009 Novell, Inc. + * Copyright (C) 2009 - 2010 Red Hat, Inc. + */ + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <errno.h> +#include <ctype.h> + +#include "mm-modem-novatel-cdma.h" +#include "mm-errors.h" +#include "mm-callback-info.h" + +static void modem_cdma_init (MMModemCdma *cdma_class); + +G_DEFINE_TYPE_EXTENDED (MMModemNovatelCdma, mm_modem_novatel_cdma, MM_TYPE_GENERIC_CDMA, 0, + G_IMPLEMENT_INTERFACE (MM_TYPE_MODEM_CDMA, modem_cdma_init)) + + +MMModem * +mm_modem_novatel_cdma_new (const char *device, + const char *driver, + const char *plugin, + gboolean evdo_rev0, + gboolean evdo_revA) +{ + g_return_val_if_fail (device != NULL, NULL); + g_return_val_if_fail (driver != NULL, NULL); + g_return_val_if_fail (plugin != NULL, NULL); + + return MM_MODEM (g_object_new (MM_TYPE_MODEM_NOVATEL_CDMA, + MM_MODEM_MASTER_DEVICE, device, + MM_MODEM_DRIVER, driver, + MM_MODEM_PLUGIN, plugin, + MM_GENERIC_CDMA_EVDO_REV0, evdo_rev0, + MM_GENERIC_CDMA_EVDO_REVA, evdo_revA, + NULL)); +} + +/*****************************************************************************/ + +static void +parent_csq_done (MMModem *modem, + guint32 result, + GError *error, + gpointer user_data) +{ + MMCallbackInfo *info = user_data; + + if (error) + info->error = g_error_copy (error); + else + mm_callback_info_set_result (info, GUINT_TO_POINTER (result), NULL); + mm_callback_info_schedule (info); +} + +static int +get_one_qual (const char *reply, const char *tag) +{ + int qual = -1; + const char *p; + + p = strstr (reply, tag); + if (!p) + return -1; + + /* Skip the tag */ + p += strlen (tag); + + /* Skip spaces */ + while (isspace (*p)) + p++; + if (*p == '-') { + long int dbm; + + errno = 0; + dbm = strtol (p, NULL, 10); + if (dbm < 0 && errno == 0) { + dbm = CLAMP (dbm, -113, -51); + qual = 100 - ((dbm + 51) * 100 / (-113 + 51)); + } + } + + return qual; +} + +static void +get_rssi_done (MMAtSerialPort *port, + GString *response, + GError *error, + gpointer user_data) +{ + MMCallbackInfo *info = (MMCallbackInfo *) user_data; + MMModemCdma *parent_iface; + int qual; + + info->error = mm_modem_check_removed (info->modem, error); + if (info->error) { + if (info->modem) { + /* Fallback to parent's method */ + g_clear_error (&info->error); + parent_iface = g_type_interface_peek_parent (MM_MODEM_CDMA_GET_INTERFACE (info->modem)); + parent_iface->get_signal_quality (MM_MODEM_CDMA (info->modem), parent_csq_done, info); + } else + mm_callback_info_schedule (info); + + return; + } + + /* Parse the signal quality */ + qual = get_one_qual (response->str, "RX0="); + if (qual < 0) + qual = get_one_qual (response->str, "RX1="); + + if (qual >= 0) { + mm_callback_info_set_result (info, GUINT_TO_POINTER ((guint32) qual), NULL); + mm_generic_cdma_update_cdma1x_quality (MM_GENERIC_CDMA (info->modem), (guint32) qual); + } else { + info->error = g_error_new (MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL, + "%s", "Could not parse signal quality results"); + } + + mm_callback_info_schedule (info); +} + +static void +get_signal_quality (MMModemCdma *modem, + MMModemUIntFn callback, + gpointer user_data) +{ + MMCallbackInfo *info; + MMAtSerialPort *port; + MMModemCdma *parent_iface; + + port = mm_generic_cdma_get_best_at_port (MM_GENERIC_CDMA (modem), NULL); + if (!port) { + /* Let the superclass handle it */ + parent_iface = g_type_interface_peek_parent (MM_MODEM_CDMA_GET_INTERFACE (modem)); + parent_iface->get_signal_quality (MM_MODEM_CDMA (modem), callback, user_data); + return; + } + + info = mm_callback_info_uint_new (MM_MODEM (modem), callback, user_data); + + /* Many Novatel CDMA cards don't report CSQ in standard 0 - 31 and the CSQ + * reply doesn't appear to be in positive dBm either; instead try the custom + * Novatel command for it. + */ + mm_at_serial_port_queue_command (port, "$NWRSSI", 3, get_rssi_done, info); +} + +/*****************************************************************************/ + +static void +modem_cdma_init (MMModemCdma *cdma_class) +{ + cdma_class->get_signal_quality = get_signal_quality; +} + +static void +mm_modem_novatel_cdma_init (MMModemNovatelCdma *self) +{ +} + +static void +mm_modem_novatel_cdma_class_init (MMModemNovatelCdmaClass *klass) +{ + mm_modem_novatel_cdma_parent_class = g_type_class_peek_parent (klass); +} + diff --git a/plugins/mm-modem-novatel-cdma.h b/plugins/mm-modem-novatel-cdma.h new file mode 100644 index 0000000..4d38d8e --- /dev/null +++ b/plugins/mm-modem-novatel-cdma.h @@ -0,0 +1,45 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details: + * + * Copyright (C) 2008 - 2009 Novell, Inc. + * Copyright (C) 2009 - 2010 Red Hat, Inc. + */ + +#ifndef MM_MODEM_NOVATEL_CDMA_H +#define MM_MODEM_NOVATEL_CDMA_H + +#include "mm-generic-cdma.h" + +#define MM_TYPE_MODEM_NOVATEL_CDMA (mm_modem_novatel_cdma_get_type ()) +#define MM_MODEM_NOVATEL_CDMA(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_MODEM_NOVATEL_CDMA, MMModemNovatelCdma)) +#define MM_MODEM_NOVATEL_CDMA_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_MODEM_NOVATEL_CDMA, MMModemNovatelCdmaClass)) +#define MM_IS_MODEM_NOVATEL_CDMA(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_MODEM_NOVATEL_CDMA)) +#define MM_IS_MODEM_NOVATEL_CDMA_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_MODEM_NOVATEL_CDMA)) +#define MM_MODEM_NOVATEL_CDMA_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_MODEM_NOVATEL_CDMA, MMModemNovatelCdmaClass)) + +typedef struct { + MMGenericCdma parent; +} MMModemNovatelCdma; + +typedef struct { + MMGenericCdmaClass parent; +} MMModemNovatelCdmaClass; + +GType mm_modem_novatel_cdma_get_type (void); + +MMModem *mm_modem_novatel_cdma_new (const char *device, + const char *driver, + const char *plugin, + gboolean evdo_rev0, + gboolean evdo_revA); + +#endif /* MM_MODEM_NOVATEL_CDMA_H */ diff --git a/plugins/mm-modem-novatel-gsm.c b/plugins/mm-modem-novatel-gsm.c index 8189627..584156f 100644 --- a/plugins/mm-modem-novatel-gsm.c +++ b/plugins/mm-modem-novatel-gsm.c @@ -11,9 +11,10 @@ * GNU General Public License for more details: * * Copyright (C) 2008 - 2009 Novell, Inc. - * Copyright (C) 2009 Red Hat, Inc. + * Copyright (C) 2009 - 2010 Red Hat, Inc. */ +#include <config.h> #include <stdlib.h> #include <stdio.h> #include <string.h> @@ -21,6 +22,7 @@ #include "mm-modem-novatel-gsm.h" #include "mm-errors.h" #include "mm-callback-info.h" +#include "mm-modem-helpers.h" static void modem_init (MMModem *modem_class); @@ -49,114 +51,246 @@ mm_modem_novatel_gsm_new (const char *device, /*****************************************************************************/ static void -init_modem_done (MMSerialPort *port, - GString *response, - GError *error, - gpointer user_data) +dmat_callback2 (MMAtSerialPort *port, + GString *response, + GError *error, + gpointer user_data) { - MMCallbackInfo *info = (MMCallbackInfo *) user_data; - - mm_generic_gsm_enable_complete (MM_GENERIC_GSM (info->modem), error, info); + mm_serial_port_close (MM_SERIAL_PORT (port)); } static void -pin_check_done (MMModem *modem, GError *error, gpointer user_data) +dmat_callback (MMAtSerialPort *port, + GString *response, + GError *error, + gpointer user_data) { - MMCallbackInfo *info = (MMCallbackInfo *) user_data; - MMGenericGsm *self = MM_GENERIC_GSM (modem); - MMSerialPort *primary; - if (error) { - mm_generic_gsm_enable_complete (self, error, info); - return; + /* Try it again */ + if (mm_serial_port_open (MM_SERIAL_PORT (port), NULL)) + mm_at_serial_port_queue_command (port, "$NWDMAT=1", 2, dmat_callback2, NULL); + } + + mm_serial_port_close (MM_SERIAL_PORT (port)); +} + +static gboolean +grab_port (MMModem *modem, + const char *subsys, + const char *name, + MMPortType suggested_type, + gpointer user_data, + GError **error) +{ + MMGenericGsm *gsm = MM_GENERIC_GSM (modem); + MMPortType ptype = MM_PORT_TYPE_IGNORED; + MMPort *port = NULL; + + if (suggested_type == MM_PORT_TYPE_UNKNOWN) { + if (!mm_generic_gsm_get_at_port (gsm, MM_PORT_TYPE_PRIMARY)) + ptype = MM_PORT_TYPE_PRIMARY; + else if (!mm_generic_gsm_get_at_port (gsm, MM_PORT_TYPE_SECONDARY)) + ptype = MM_PORT_TYPE_SECONDARY; + } else + ptype = suggested_type; + + port = mm_generic_gsm_grab_port (gsm, subsys, name, ptype, error); + if (port && MM_IS_AT_SERIAL_PORT (port) && (ptype == MM_PORT_TYPE_PRIMARY)) { + /* Flip secondary ports to AT mode */ + if (mm_serial_port_open (MM_SERIAL_PORT (port), NULL)) + mm_at_serial_port_queue_command (MM_AT_SERIAL_PORT (port), "$NWDMAT=1", 2, dmat_callback, NULL); } - /* Finish the initialization */ - primary = mm_generic_gsm_get_port (self, MM_PORT_TYPE_PRIMARY); - g_assert (primary); - mm_serial_port_queue_command (primary, "Z E0 V1 X4 &C1 +CMEE=1;+CFUN=1", 10, init_modem_done, info); + return !!port; } +/*****************************************************************************/ + static void -pre_init_done (MMSerialPort *port, - GString *response, - GError *error, - gpointer user_data) +set_allowed_mode_done (MMAtSerialPort *port, + GString *response, + GError *error, + gpointer user_data) { MMCallbackInfo *info = (MMCallbackInfo *) user_data; - if (error) { - mm_generic_gsm_enable_complete (MM_GENERIC_GSM (info->modem), error, info); + if (error) + info->error = g_error_copy (error); + + mm_callback_info_schedule (info); +} + +static void +set_allowed_mode (MMGenericGsm *gsm, + MMModemGsmAllowedMode mode, + MMModemFn callback, + gpointer user_data) +{ + MMCallbackInfo *info; + MMAtSerialPort *port; + char *command; + int nw_mode = 0; /* 3G preferred */ + + info = mm_callback_info_new (MM_MODEM (gsm), callback, user_data); + + port = mm_generic_gsm_get_best_at_port (gsm, &info->error); + if (!port) { + mm_callback_info_schedule (info); return; } - /* Now check the PIN explicitly, novatel doesn't seem to report - * that it needs it otherwise. - */ - mm_generic_gsm_check_pin (MM_GENERIC_GSM (info->modem), pin_check_done, info); + switch (mode) { + case MM_MODEM_GSM_ALLOWED_MODE_2G_ONLY: + nw_mode = 1; + break; + case MM_MODEM_GSM_ALLOWED_MODE_3G_ONLY: + nw_mode = 2; + break; + case MM_MODEM_GSM_ALLOWED_MODE_2G_PREFERRED: + case MM_MODEM_GSM_ALLOWED_MODE_3G_PREFERRED: + case MM_MODEM_GSM_ALLOWED_MODE_ANY: + default: + break; + } + + command = g_strdup_printf ("$NWRAT=%d,2", nw_mode); + mm_at_serial_port_queue_command (port, command, 3, set_allowed_mode_done, info); + g_free (command); +} + +static gboolean +parse_nwrat_response (GString *response, + MMModemGsmAllowedMode *out_mode, + GError **error) +{ + GRegex *r; + GMatchInfo *match_info; + char *str; + gint mode = -1; + gboolean success = FALSE; + + g_return_val_if_fail (response != NULL, FALSE); + g_return_val_if_fail (out_mode != NULL, FALSE); + + r = g_regex_new ("\\$NWRAT:\\s*(\\d),(\\d),(\\d)", G_REGEX_UNGREEDY, 0, NULL); + if (!r) { + g_set_error_literal (error, MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL, + "Internal error parsing mode/tech response"); + return FALSE; + } + + if (!g_regex_match_full (r, response->str, response->len, 0, 0, &match_info, NULL)) { + g_set_error_literal (error, MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL, + "Failed to parse mode/tech response"); + goto out; + } + + str = g_match_info_fetch (match_info, 1); + mode = atoi (str); + g_free (str); + + g_match_info_free (match_info); + + if (mode < 0 || mode > 2) { + g_set_error_literal (error, MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL, + "Failed to parse mode/tech response"); + goto out; + } + + if (out_mode) { + if (mode == 0) + *out_mode = MM_MODEM_GSM_ALLOWED_MODE_3G_PREFERRED; + else if (mode == 1) + *out_mode = MM_MODEM_GSM_ALLOWED_MODE_2G_ONLY; + else if (mode == 2) + *out_mode = MM_MODEM_GSM_ALLOWED_MODE_3G_ONLY; + else + *out_mode = MM_MODEM_GSM_ALLOWED_MODE_ANY; + } + success = TRUE; + +out: + g_regex_unref (r); + return success; } static void -enable_flash_done (MMSerialPort *port, GError *error, gpointer user_data) +get_allowed_mode_done (MMAtSerialPort *port, + GString *response, + GError *error, + gpointer user_data) { - MMCallbackInfo *info = user_data; + MMCallbackInfo *info = (MMCallbackInfo *) user_data; + MMModemGsmAllowedMode mode = MM_MODEM_GSM_ALLOWED_MODE_ANY; - if (error) - mm_generic_gsm_enable_complete (MM_GENERIC_GSM (info->modem), error, info); - else - mm_serial_port_queue_command (port, "E0 V1", 3, pre_init_done, user_data); + info->error = mm_modem_check_removed (info->modem, error); + if (!info->error) { + parse_nwrat_response (response, &mode, &info->error); + mm_callback_info_set_result (info, GUINT_TO_POINTER (mode), NULL); + } + + mm_callback_info_schedule (info); } static void -do_enable (MMGenericGsm *modem, MMModemFn callback, gpointer user_data) +get_allowed_mode (MMGenericGsm *gsm, + MMModemUIntFn callback, + gpointer user_data) { MMCallbackInfo *info; - MMSerialPort *primary; + MMAtSerialPort *port; - primary = mm_generic_gsm_get_port (modem, MM_PORT_TYPE_PRIMARY); - g_assert (primary); + info = mm_callback_info_uint_new (MM_MODEM (gsm), callback, user_data); - info = mm_callback_info_new (MM_MODEM (modem), callback, user_data); - mm_serial_port_flash (primary, 100, enable_flash_done, info); + port = mm_generic_gsm_get_best_at_port (gsm, &info->error); + if (!port) { + mm_callback_info_schedule (info); + return; + } + + mm_at_serial_port_queue_command (port, "$NWRAT?", 3, get_allowed_mode_done, info); } static void -dmat_callback (MMSerialPort *port, - GString *response, - GError *error, - gpointer user_data) +get_act_request_done (MMAtSerialPort *port, + GString *response, + GError *error, + gpointer user_data) { - mm_serial_port_close (port); + MMCallbackInfo *info = user_data; + MMModemGsmAccessTech act = MM_MODEM_GSM_ACCESS_TECH_UNKNOWN; + const char *p; + + if (error) + info->error = g_error_copy (error); + else { + p = mm_strip_tag (response->str, "$CNTI:"); + p = strchr (p, ','); + if (p) + act = mm_gsm_string_to_access_tech (p + 1); + } + + mm_callback_info_set_result (info, GUINT_TO_POINTER (act), NULL); + mm_callback_info_schedule (info); } -static gboolean -grab_port (MMModem *modem, - const char *subsys, - const char *name, - MMPortType suggested_type, - gpointer user_data, - GError **error) +static void +get_access_technology (MMGenericGsm *modem, + MMModemUIntFn callback, + gpointer user_data) { - MMGenericGsm *gsm = MM_GENERIC_GSM (modem); - MMPortType ptype = MM_PORT_TYPE_IGNORED; - MMPort *port = NULL; + MMAtSerialPort *port; + MMCallbackInfo *info; - if (suggested_type == MM_PORT_TYPE_UNKNOWN) { - if (!mm_generic_gsm_get_port (gsm, MM_PORT_TYPE_PRIMARY)) - ptype = MM_PORT_TYPE_PRIMARY; - else if (!mm_generic_gsm_get_port (gsm, MM_PORT_TYPE_SECONDARY)) - ptype = MM_PORT_TYPE_SECONDARY; - } else - ptype = suggested_type; + info = mm_callback_info_uint_new (MM_MODEM (modem), callback, user_data); - port = mm_generic_gsm_grab_port (gsm, subsys, name, ptype, error); - if (port && MM_IS_SERIAL_PORT (port) && (ptype == MM_PORT_TYPE_PRIMARY)) { - /* Flip secondary ports to AT mode */ - if (mm_serial_port_open (MM_SERIAL_PORT (port), NULL)) - mm_serial_port_queue_command (MM_SERIAL_PORT (port), "$NWDMAT=1", 2, dmat_callback, NULL); + port = mm_generic_gsm_get_best_at_port (modem, &info->error); + if (!port) { + mm_callback_info_schedule (info); + return; } - return !!port; + mm_at_serial_port_queue_command (port, "$CNTI=0", 3, get_act_request_done, info); } /*****************************************************************************/ @@ -179,6 +313,8 @@ mm_modem_novatel_gsm_class_init (MMModemNovatelGsmClass *klass) mm_modem_novatel_gsm_parent_class = g_type_class_peek_parent (klass); - gsm_class->do_enable = do_enable; + gsm_class->set_allowed_mode = set_allowed_mode; + gsm_class->get_allowed_mode = get_allowed_mode; + gsm_class->get_access_technology = get_access_technology; } diff --git a/plugins/mm-modem-option-utils.c b/plugins/mm-modem-option-utils.c new file mode 100644 index 0000000..35dd1ac --- /dev/null +++ b/plugins/mm-modem-option-utils.c @@ -0,0 +1,451 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details: + * + * Copyright (C) 2010 Red Hat, Inc. + */ + +/****************************************** + * Generic utilities for Option NV modems + * Used with both 'option' and 'hso' + ******************************************/ + +#include "mm-callback-info.h" +#include "mm-at-serial-port.h" +#include "mm-generic-gsm.h" +#include "mm-modem-helpers.h" + +static void +option_get_allowed_mode_done (MMAtSerialPort *port, + GString *response, + GError *error, + gpointer user_data) +{ + MMCallbackInfo *info = (MMCallbackInfo *) user_data; + gboolean parsed = FALSE; + + if (error) + info->error = g_error_copy (error); + else if (!g_str_has_prefix (response->str, "_OPSYS: ")) { + int a, b; + + if (sscanf (response->str + 8, "%d,%d", &a, &b)) { + MMModemGsmAllowedMode mode = MM_MODEM_GSM_ALLOWED_MODE_ANY; + + switch (a) { + case 0: + mode = MM_MODEM_GSM_ALLOWED_MODE_2G_ONLY; + break; + case 1: + mode = MM_MODEM_GSM_ALLOWED_MODE_3G_ONLY; + break; + case 2: + mode = MM_MODEM_GSM_ALLOWED_MODE_2G_PREFERRED; + break; + case 3: + mode = MM_MODEM_GSM_ALLOWED_MODE_3G_PREFERRED; + break; + default: + break; + } + + mm_callback_info_set_result (info, GUINT_TO_POINTER (mode), NULL); + parsed = TRUE; + } + } + + if (!error && !parsed) + info->error = g_error_new_literal (MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL, + "Could not parse allowed mode results"); + + mm_callback_info_schedule (info); +} + +static void +option_get_allowed_mode (MMGenericGsm *gsm, + MMModemUIntFn callback, + gpointer user_data) +{ + MMCallbackInfo *info; + MMAtSerialPort *port; + + info = mm_callback_info_uint_new (MM_MODEM (gsm), callback, user_data); + + port = mm_generic_gsm_get_best_at_port (gsm, &info->error); + if (!port) { + mm_callback_info_schedule (info); + return; + } + mm_at_serial_port_queue_command (port, "AT_OPSYS?", 3, option_get_allowed_mode_done, info); +} + +static void +option_set_allowed_mode_done (MMAtSerialPort *port, + GString *response, + GError *error, + gpointer user_data) +{ + MMCallbackInfo *info = (MMCallbackInfo *) user_data; + + if (error) + info->error = g_error_copy (error); + + mm_callback_info_schedule (info); +} + +static void +option_set_allowed_mode (MMGenericGsm *gsm, + MMModemGsmAllowedMode mode, + MMModemFn callback, + gpointer user_data) +{ + MMCallbackInfo *info; + MMAtSerialPort *port; + char *command; + int i; + + info = mm_callback_info_new (MM_MODEM (gsm), callback, user_data); + + port = mm_generic_gsm_get_best_at_port (gsm, &info->error); + if (!port) { + mm_callback_info_schedule (info); + return; + } + + switch (mode) { + case MM_MODEM_GSM_ALLOWED_MODE_2G_ONLY: + i = 0; + break; + case MM_MODEM_GSM_ALLOWED_MODE_3G_ONLY: + i = 1; + break; + case MM_MODEM_GSM_ALLOWED_MODE_2G_PREFERRED: + i = 2; + break; + case MM_MODEM_GSM_ALLOWED_MODE_3G_PREFERRED: + i = 3; + break; + case MM_MODEM_GSM_ALLOWED_MODE_ANY: + default: + i = 5; + break; + } + + command = g_strdup_printf ("AT_OPSYS=%d,2", i); + mm_at_serial_port_queue_command (port, command, 3, option_set_allowed_mode_done, info); + g_free (command); +} + +static gboolean +octi_to_mm (char octi, MMModemGsmAccessTech *out_act) +{ + if (octi == '1') { + *out_act = MM_MODEM_GSM_ACCESS_TECH_GSM; + return TRUE; + } else if (octi == '2') { + *out_act = MM_MODEM_GSM_ACCESS_TECH_GPRS; + return TRUE; + } else if (octi == '3') { + *out_act = MM_MODEM_GSM_ACCESS_TECH_EDGE; + return TRUE; + } + return FALSE; +} + +static gboolean +owcti_to_mm (char owcti, MMModemGsmAccessTech *out_act) +{ + if (owcti == '1') { + *out_act = MM_MODEM_GSM_ACCESS_TECH_UMTS; + return TRUE; + } else if (owcti == '2') { + *out_act = MM_MODEM_GSM_ACCESS_TECH_HSDPA; + return TRUE; + } else if (owcti == '3') { + *out_act = MM_MODEM_GSM_ACCESS_TECH_HSUPA; + return TRUE; + } else if (owcti == '4') { + *out_act = MM_MODEM_GSM_ACCESS_TECH_HSPA; + return TRUE; + } + return FALSE; +} + +static gboolean +parse_octi_response (GString *response, MMModemGsmAccessTech *act) +{ + MMModemGsmAccessTech cur_act = MM_MODEM_GSM_ACCESS_TECH_UNKNOWN; + const char *p; + GRegex *r; + GMatchInfo *match_info; + char *str; + gboolean success = FALSE; + + g_return_val_if_fail (act != NULL, FALSE); + g_return_val_if_fail (response != NULL, FALSE); + + p = mm_strip_tag (response->str, "_OCTI:"); + + r = g_regex_new ("(\\d),(\\d)", G_REGEX_UNGREEDY, 0, NULL); + g_return_val_if_fail (r != NULL, FALSE); + + g_regex_match (r, p, 0, &match_info); + if (g_match_info_matches (match_info)) { + str = g_match_info_fetch (match_info, 2); + if (str && octi_to_mm (str[0], &cur_act)) { + *act = cur_act; + success = TRUE; + } + g_free (str); + } + g_match_info_free (match_info); + g_regex_unref (r); + + return success; +} + +static void +ossys_octi_request_done (MMAtSerialPort *port, + GString *response, + GError *error, + gpointer user_data) +{ + MMModemGsmAccessTech act = MM_MODEM_GSM_ACCESS_TECH_UNKNOWN; + + if (!error) { + if (parse_octi_response (response, &act)) + mm_generic_gsm_update_access_technology (MM_GENERIC_GSM (user_data), act); + } +} + +static void +ossys_owcti_request_done (MMAtSerialPort *port, + GString *response, + GError *error, + gpointer user_data) +{ + const char *p; + MMModemGsmAccessTech act = MM_MODEM_GSM_ACCESS_TECH_UNKNOWN; + + if (!error) { + p = mm_strip_tag (response->str, "_OWCTI:"); + if (owcti_to_mm (*p, &act)) + mm_generic_gsm_update_access_technology (MM_GENERIC_GSM (user_data), act); + } +} + +static void +option_ossys_tech_changed (MMAtSerialPort *port, + GMatchInfo *info, + gpointer user_data) +{ + MMModemGsmAccessTech act = MM_MODEM_GSM_ACCESS_TECH_UNKNOWN; + char *str; + + str = g_match_info_fetch (info, 1); + if (str) { + switch (atoi (str)) { + case 0: + act = MM_MODEM_GSM_ACCESS_TECH_GPRS; + break; + case 2: + act = MM_MODEM_GSM_ACCESS_TECH_UMTS; + break; + default: + break; + } + } + g_free (str); + + mm_generic_gsm_update_access_technology (MM_GENERIC_GSM (user_data), act); + + /* _OSSYSI only indicates general 2G/3G mode, so queue up some explicit + * access technology requests. + */ + if (act == MM_MODEM_GSM_ACCESS_TECH_GPRS) + mm_at_serial_port_queue_command (port, "_OCTI?", 3, ossys_octi_request_done, user_data); + else if (act == MM_MODEM_GSM_ACCESS_TECH_UMTS) + mm_at_serial_port_queue_command (port, "_OWCTI?", 3, ossys_owcti_request_done, user_data); +} + +static void +option_2g_tech_changed (MMAtSerialPort *port, + GMatchInfo *match_info, + gpointer user_data) +{ + MMModemGsmAccessTech act = MM_MODEM_GSM_ACCESS_TECH_UNKNOWN; + char *str; + + str = g_match_info_fetch (match_info, 1); + if (octi_to_mm (str[0], &act)) + mm_generic_gsm_update_access_technology (MM_GENERIC_GSM (user_data), act); + g_free (str); +} + +static void +option_3g_tech_changed (MMAtSerialPort *port, + GMatchInfo *match_info, + gpointer user_data) +{ + MMModemGsmAccessTech act = MM_MODEM_GSM_ACCESS_TECH_UNKNOWN; + char *str; + + str = g_match_info_fetch (match_info, 1); + if (owcti_to_mm (str[0], &act)) + mm_generic_gsm_update_access_technology (MM_GENERIC_GSM (user_data), act); + g_free (str); +} + +static void +option_signal_changed (MMAtSerialPort *port, + GMatchInfo *match_info, + gpointer user_data) +{ + char *str; + int quality = 0; + + str = g_match_info_fetch (match_info, 1); + quality = atoi (str); + g_free (str); + + if (quality == 99) { + /* 99 means unknown */ + quality = 0; + } else { + /* Normalize the quality */ + quality = CLAMP (quality, 0, 31) * 100 / 31; + } + + mm_generic_gsm_update_signal_quality (MM_GENERIC_GSM (user_data), (guint32) quality); +} + +static void +option_register_unsolicted_handlers (MMGenericGsm *modem, MMAtSerialPort *port) +{ + GRegex *regex; + + regex = g_regex_new ("\\r\\n_OSSYSI:\\s*(\\d+)\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); + mm_at_serial_port_add_unsolicited_msg_handler (port, regex, option_ossys_tech_changed, modem, NULL); + g_regex_unref (regex); + + regex = g_regex_new ("\\r\\n_OCTI:\\s*(\\d+)\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); + mm_at_serial_port_add_unsolicited_msg_handler (port, regex, option_2g_tech_changed, modem, NULL); + g_regex_unref (regex); + + regex = g_regex_new ("\\r\\n_OUWCTI:\\s*(\\d+)\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); + mm_at_serial_port_add_unsolicited_msg_handler (port, regex, option_3g_tech_changed, modem, NULL); + g_regex_unref (regex); + + regex = g_regex_new ("\\r\\n_OSIGQ:\\s*(\\d+),(\\d)\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); + mm_at_serial_port_add_unsolicited_msg_handler (port, regex, option_signal_changed, modem, NULL); + g_regex_unref (regex); +} + +static void +unsolicited_msg_done (MMAtSerialPort *port, + GString *response, + GError *error, + gpointer user_data) +{ + MMCallbackInfo *info = user_data; + + if (info) + mm_callback_info_chain_complete_one (info); +} + +static void +option_change_unsolicited_messages (MMGenericGsm *modem, + gboolean enabled, + MMModemFn callback, + gpointer user_data) +{ + MMCallbackInfo *info = NULL; + MMAtSerialPort *primary; + + if (callback) { + info = mm_callback_info_new (MM_MODEM (modem), callback, user_data); + mm_callback_info_chain_start (info, 4); + } + + primary = mm_generic_gsm_get_at_port (modem, MM_PORT_TYPE_PRIMARY); + g_assert (primary); + + mm_at_serial_port_queue_command (primary, enabled ? "_OSSYS=1" : "_OSSYS=0", 3, unsolicited_msg_done, info); + mm_at_serial_port_queue_command (primary, enabled ? "_OCTI=1" : "_OCTI=0", 3, unsolicited_msg_done, info); + mm_at_serial_port_queue_command (primary, enabled ? "_OUWCTI=1" : "_OUWCTI=0", 3, unsolicited_msg_done, info); + mm_at_serial_port_queue_command (primary, enabled ? "_OSQI=1" : "_OSQI=0", 3, unsolicited_msg_done, info); +} + +static void +get_act_octi_request_done (MMAtSerialPort *port, + GString *response, + GError *error, + gpointer user_data) +{ + MMCallbackInfo *info = user_data; + MMModemGsmAccessTech octi = MM_MODEM_GSM_ACCESS_TECH_UNKNOWN; + MMModemGsmAccessTech owcti; + + if (!error) { + if (parse_octi_response (response, &octi)) { + /* If no 3G tech yet or current tech isn't 3G, then 2G tech is the best */ + owcti = GPOINTER_TO_UINT (mm_callback_info_get_data (info, "owcti")); + if (octi && !owcti) + mm_callback_info_set_result (info, GUINT_TO_POINTER (octi), NULL); + } + } + + mm_callback_info_chain_complete_one (info); +} + +static void +get_act_owcti_request_done (MMAtSerialPort *port, + GString *response, + GError *error, + gpointer user_data) +{ + MMCallbackInfo *info = user_data; + MMModemGsmAccessTech owcti = MM_MODEM_GSM_ACCESS_TECH_UNKNOWN; + const char *p; + + if (!error) { + p = mm_strip_tag (response->str, "_OWCTI:"); + if (owcti_to_mm (*p, &owcti)) { + /* 3G tech always takes precedence over 2G tech */ + if (owcti) + mm_callback_info_set_result (info, GUINT_TO_POINTER (owcti), NULL); + } + } + + mm_callback_info_chain_complete_one (info); +} + +static void +option_get_access_technology (MMGenericGsm *modem, + MMModemUIntFn callback, + gpointer user_data) +{ + MMAtSerialPort *port; + MMCallbackInfo *info; + + info = mm_callback_info_uint_new (MM_MODEM (modem), callback, user_data); + mm_callback_info_chain_start (info, 2); + + port = mm_generic_gsm_get_best_at_port (modem, &info->error); + if (!port) { + mm_callback_info_schedule (info); + return; + } + + mm_at_serial_port_queue_command (port, "_OCTI?", 3, get_act_octi_request_done, info); + mm_at_serial_port_queue_command (port, "_OWCTI?", 3, get_act_owcti_request_done, info); +} + diff --git a/plugins/mm-modem-option.c b/plugins/mm-modem-option.c index 2076ae6..ac04b0b 100644 --- a/plugins/mm-modem-option.c +++ b/plugins/mm-modem-option.c @@ -23,12 +23,15 @@ #include "mm-callback-info.h" static void modem_init (MMModem *modem_class); -static void modem_gsm_network_init (MMModemGsmNetwork *gsm_network_class); G_DEFINE_TYPE_EXTENDED (MMModemOption, mm_modem_option, MM_TYPE_GENERIC_GSM, 0, - G_IMPLEMENT_INTERFACE (MM_TYPE_MODEM, modem_init) - G_IMPLEMENT_INTERFACE (MM_TYPE_MODEM_GSM_NETWORK, modem_gsm_network_init)) + G_IMPLEMENT_INTERFACE (MM_TYPE_MODEM, modem_init)) +#define MM_MODEM_OPTION_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), MM_TYPE_MODEM_OPTION, MMModemOptionPrivate)) + +typedef struct { + guint enable_wait_id; +} MMModemOptionPrivate; MMModem * mm_modem_option_new (const char *device, @@ -46,174 +49,156 @@ mm_modem_option_new (const char *device, NULL)); } +#include "mm-modem-option-utils.c" + /*****************************************************************************/ -static void -pin_check_done (MMModem *modem, GError *error, gpointer user_data) +static gboolean +option_enabled (gpointer user_data) { - MMCallbackInfo *info = (MMCallbackInfo *) user_data; + MMCallbackInfo *info = user_data; + MMGenericGsm *modem; + MMModemOptionPrivate *priv; - mm_generic_gsm_enable_complete (MM_GENERIC_GSM (modem), error, info); -} + /* Make sure we don't use an invalid modem that may have been removed */ + if (info->modem) { + modem = MM_GENERIC_GSM (info->modem); + priv = MM_MODEM_OPTION_GET_PRIVATE (modem); + priv->enable_wait_id = 0; -static gboolean -option_enabled (gpointer data) -{ - MMCallbackInfo *info = (MMCallbackInfo *) data; + option_change_unsolicited_messages (modem, TRUE, NULL, NULL); - /* Now check the PIN explicitly, option doesn't seem to report - * that it needs it otherwise. - */ - mm_generic_gsm_check_pin (MM_GENERIC_GSM (info->modem), pin_check_done, info); + MM_GENERIC_GSM_CLASS (mm_modem_option_parent_class)->do_enable_power_up_done (modem, NULL, NULL, info); + } return FALSE; } static void -parent_enable_done (MMModem *modem, GError *error, gpointer user_data) +real_do_enable_power_up_done (MMGenericGsm *gsm, + GString *response, + GError *error, + MMCallbackInfo *info) { - MMCallbackInfo *info = (MMCallbackInfo *) user_data; + MMModemOptionPrivate *priv = MM_MODEM_OPTION_GET_PRIVATE (gsm); if (error) { - mm_generic_gsm_enable_complete (MM_GENERIC_GSM (modem), error, info); + /* Chain up to parent */ + MM_GENERIC_GSM_CLASS (mm_modem_option_parent_class)->do_enable_power_up_done (gsm, NULL, error, info); return; } - /* Option returns OK on +CFUN=1 right away but needs some time - * to finish initialization + /* Some Option devices return OK on +CFUN=1 right away but need some time + * to finish initialization. */ - g_timeout_add_seconds (10, option_enabled, info); + g_warn_if_fail (priv->enable_wait_id == 0); + priv->enable_wait_id = g_timeout_add_seconds (10, option_enabled, info); } +/*****************************************************************************/ + static void -enable (MMModem *modem, - MMModemFn callback, - gpointer user_data) +get_allowed_mode (MMGenericGsm *gsm, + MMModemUIntFn callback, + gpointer user_data) { - MMModem *parent_modem_iface; - MMCallbackInfo *info; + option_get_allowed_mode (gsm, callback, user_data); +} - info = mm_callback_info_new (modem, callback, user_data); - parent_modem_iface = g_type_interface_peek_parent (MM_MODEM_GET_INTERFACE (modem)); - parent_modem_iface->enable (modem, parent_enable_done, info); +static void +set_allowed_mode (MMGenericGsm *gsm, + MMModemGsmAllowedMode mode, + MMModemFn callback, + gpointer user_data) +{ + option_set_allowed_mode (gsm, mode, callback, user_data); } static void -get_network_mode_done (MMSerialPort *port, - GString *response, - GError *error, +get_access_technology (MMGenericGsm *gsm, + MMModemUIntFn callback, gpointer user_data) { + option_get_access_technology (gsm, callback, user_data); +} + +/*****************************************************************************/ + +static void +parent_disable_done (MMModem *modem, GError *error, gpointer user_data) +{ MMCallbackInfo *info = (MMCallbackInfo *) user_data; - gboolean parsed = FALSE; if (error) info->error = g_error_copy (error); - else if (!g_str_has_prefix (response->str, "_OPSYS: ")) { - int a, b; - - if (sscanf (response->str + 8, "%d,%d", &a, &b)) { - MMModemGsmMode mode = MM_MODEM_GSM_MODE_ANY; - - switch (a) { - case 0: - mode = MM_MODEM_GSM_MODE_2G_ONLY; - break; - case 1: - mode = MM_MODEM_GSM_MODE_3G_ONLY; - break; - case 2: - mode = MM_MODEM_GSM_MODE_2G_PREFERRED; - break; - case 3: - mode = MM_MODEM_GSM_MODE_3G_PREFERRED; - break; - default: - break; - } - - mm_callback_info_set_result (info, GUINT_TO_POINTER (mode), NULL); - parsed = TRUE; - } - } - - if (!error && !parsed) - info->error = g_error_new_literal (MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL, - "Could not parse network mode results"); - mm_callback_info_schedule (info); } static void -get_network_mode (MMModemGsmNetwork *modem, - MMModemUIntFn callback, - gpointer user_data) +unsolicited_disable_done (MMModem *modem, + GError *error, + gpointer user_data) { - MMCallbackInfo *info; - MMSerialPort *primary; + MMCallbackInfo *info = user_data; + MMModem *parent_modem_iface; + GError *tmp_error = NULL; + + /* Handle modem removal, but ignore other errors */ + if (g_error_matches (error, MM_MODEM_ERROR, MM_MODEM_ERROR_REMOVED)) { + parent_disable_done (modem, error, user_data); + return; + } else if (!modem) { + tmp_error = g_error_new_literal (MM_MODEM_ERROR, + MM_MODEM_ERROR_REMOVED, + "The modem was removed."); + parent_disable_done (modem, tmp_error, user_data); + g_error_free (tmp_error); + return; + } - info = mm_callback_info_uint_new (MM_MODEM (modem), callback, user_data); - primary = mm_generic_gsm_get_port (MM_GENERIC_GSM (modem), MM_PORT_TYPE_PRIMARY); - g_assert (primary); - mm_serial_port_queue_command (primary, "AT_OPSYS?", 3, get_network_mode_done, info); + /* Chain up to parent */ + parent_modem_iface = g_type_interface_peek_parent (MM_MODEM_GET_INTERFACE (modem)); + parent_modem_iface->disable (info->modem, parent_disable_done, info); } static void -set_network_mode_done (MMSerialPort *port, - GString *response, - GError *error, - gpointer user_data) +disable (MMModem *modem, + MMModemFn callback, + gpointer user_data) { - MMCallbackInfo *info = (MMCallbackInfo *) user_data; + MMCallbackInfo *info; - if (error) - info->error = g_error_copy (error); - - mm_callback_info_schedule (info); + mm_generic_gsm_pending_registration_stop (MM_GENERIC_GSM (modem)); + + info = mm_callback_info_new (modem, callback, user_data); + + /* Turn off unsolicited messages so they don't pile up in the modem */ + option_change_unsolicited_messages (MM_GENERIC_GSM (modem), FALSE, unsolicited_disable_done, info); } -static void -set_network_mode (MMModemGsmNetwork *modem, - MMModemGsmMode mode, - MMModemFn callback, - gpointer user_data) +static gboolean +grab_port (MMModem *modem, + const char *subsys, + const char *name, + MMPortType suggested_type, + gpointer user_data, + GError **error) { - MMCallbackInfo *info; - MMSerialPort *primary; - char *command; - int i; - - info = mm_callback_info_new (MM_MODEM (modem), callback, user_data); - - switch (mode) { - case MM_MODEM_GSM_MODE_ANY: - case MM_MODEM_GSM_MODE_GPRS: - case MM_MODEM_GSM_MODE_EDGE: - case MM_MODEM_GSM_MODE_2G_ONLY: - i = 0; - break; - case MM_MODEM_GSM_MODE_UMTS: - case MM_MODEM_GSM_MODE_HSDPA: - case MM_MODEM_GSM_MODE_HSUPA: - case MM_MODEM_GSM_MODE_HSPA: - case MM_MODEM_GSM_MODE_3G_ONLY: - i = 1; - break; - case MM_MODEM_GSM_MODE_2G_PREFERRED: - i = 2; - break; - case MM_MODEM_GSM_MODE_3G_PREFERRED: - i = 3; - break; - default: - i = 5; - break; + MMGenericGsm *gsm = MM_GENERIC_GSM (modem); + MMPort *port = NULL; + + port = mm_generic_gsm_grab_port (gsm, subsys, name, suggested_type, error); + if (port && MM_IS_AT_SERIAL_PORT (port)) { + if (mm_port_get_port_type (port) == MM_PORT_TYPE_PRIMARY) { + GRegex *regex; + + regex = g_regex_new ("\\r\\n\\+PACSP0\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); + mm_at_serial_port_add_unsolicited_msg_handler (MM_AT_SERIAL_PORT (port), regex, NULL, NULL, NULL); + g_regex_unref (regex); + } + option_register_unsolicted_handlers (gsm, MM_AT_SERIAL_PORT (port)); } - command = g_strdup_printf ("AT_OPSYS=%d,2", i); - primary = mm_generic_gsm_get_port (MM_GENERIC_GSM (modem), MM_PORT_TYPE_PRIMARY); - g_assert (primary); - mm_serial_port_queue_command (primary, command, 3, set_network_mode_done, info); - g_free (command); + return !!port; } /*****************************************************************************/ @@ -221,24 +206,37 @@ set_network_mode (MMModemGsmNetwork *modem, static void modem_init (MMModem *modem_class) { - modem_class->enable = enable; + modem_class->disable = disable; + modem_class->grab_port = grab_port; } static void -modem_gsm_network_init (MMModemGsmNetwork *class) +mm_modem_option_init (MMModemOption *self) { - class->set_network_mode = set_network_mode; - class->get_network_mode = get_network_mode; } static void -mm_modem_option_init (MMModemOption *self) +dispose (GObject *object) { + MMModemOptionPrivate *priv = MM_MODEM_OPTION_GET_PRIVATE (object); + + if (priv->enable_wait_id) + g_source_remove (priv->enable_wait_id); } static void mm_modem_option_class_init (MMModemOptionClass *klass) { + GObjectClass *object_class = G_OBJECT_CLASS (klass); + MMGenericGsmClass *gsm_class = MM_GENERIC_GSM_CLASS (klass); + mm_modem_option_parent_class = g_type_class_peek_parent (klass); + g_type_class_add_private (object_class, sizeof (MMModemOptionPrivate)); + + object_class->dispose = dispose; + gsm_class->do_enable_power_up_done = real_do_enable_power_up_done; + gsm_class->set_allowed_mode = set_allowed_mode; + gsm_class->get_allowed_mode = get_allowed_mode; + gsm_class->get_access_technology = get_access_technology; } diff --git a/plugins/mm-modem-sierra-cdma.c b/plugins/mm-modem-sierra-cdma.c index 4f3140b..fc62bf6 100644 --- a/plugins/mm-modem-sierra-cdma.c +++ b/plugins/mm-modem-sierra-cdma.c @@ -28,6 +28,7 @@ #include "mm-callback-info.h" #include "mm-serial-port.h" #include "mm-serial-parsers.h" +#include "mm-modem-helpers.h" G_DEFINE_TYPE (MMModemSierraCdma, mm_modem_sierra_cdma, MM_TYPE_GENERIC_CDMA) @@ -75,13 +76,19 @@ mm_modem_sierra_cdma_new (const char *device, #define SYS_MODE_NO_SERVICE_TAG "NO SRV" #define SYS_MODE_EVDO_TAG "HDR" #define SYS_MODE_1X_TAG "1x" +#define SYS_MODE_CDMA_TAG "CDMA" #define EVDO_REV_TAG "HDR Revision:" #define SID_TAG "SID:" static gboolean -get_roam_value (const char *reply, const char *tag, gboolean *roaming) +get_roam_value (const char *reply, + const char *tag, + gboolean is_eri, + gboolean *out_roaming) { char *p; + gboolean success; + guint32 ind = 0; p = strstr (reply, tag); if (!p) @@ -90,11 +97,26 @@ get_roam_value (const char *reply, const char *tag, gboolean *roaming) p += strlen (tag); while (*p && isspace (*p)) p++; + + /* Use generic ERI parsing if it's an ERI */ + if (is_eri) { + success = mm_cdma_parse_eri (p, out_roaming, &ind, NULL); + if (success) { + /* Sierra redefines ERI 0, 1, and 2 */ + if (ind == 0) + *out_roaming = FALSE; /* home */ + else if (ind == 1 || ind == 2) + *out_roaming = TRUE; /* roaming */ + } + return success; + } + + /* If it's not an ERI, roaming is just true/false */ if (*p == '1') { - *roaming = TRUE; + *out_roaming = TRUE; return TRUE; } else if (*p == '0') { - *roaming = FALSE; + *out_roaming = FALSE; return TRUE; } @@ -109,8 +131,14 @@ sys_mode_has_service (SysMode mode) || mode == SYS_MODE_EVDO_REVA); } +static gboolean +sys_mode_is_evdo (SysMode mode) +{ + return (mode == SYS_MODE_EVDO_REV0 || mode == SYS_MODE_EVDO_REVA); +} + static void -status_done (MMSerialPort *port, +status_done (MMAtSerialPort *port, GString *response, GError *error, gpointer user_data) @@ -122,16 +150,16 @@ status_done (MMSerialPort *port, gboolean have_sid = FALSE; SysMode evdo_mode = SYS_MODE_UNKNOWN; SysMode sys_mode = SYS_MODE_UNKNOWN; - gboolean cdma_1x_set = FALSE, evdo_set = FALSE; + gboolean evdo_roam = FALSE, cdma1x_roam = FALSE; if (error) { - info->error = g_error_copy (error); + /* Leave superclass' reg state alone if AT!STATUS isn't supported */ goto done; } lines = g_strsplit_set (response->str, "\n\r", 0); if (!lines) { - /* Whatever, just use default registration state */ + /* Whatever, just use superclass' registration state */ goto done; } @@ -197,29 +225,10 @@ status_done (MMSerialPort *port, } /* Roaming */ - if (get_roam_value (*iter, ROAM_1X_TAG, &bool_val)) { - mm_generic_cdma_query_reg_state_set_callback_1x_state (info, - bool_val ? MM_MODEM_CDMA_REGISTRATION_STATE_ROAMING : - MM_MODEM_CDMA_REGISTRATION_STATE_HOME); - cdma_1x_set = TRUE; - } - if (get_roam_value (*iter, ROAM_EVDO_TAG, &bool_val)) { - mm_generic_cdma_query_reg_state_set_callback_evdo_state (info, - bool_val ? MM_MODEM_CDMA_REGISTRATION_STATE_ROAMING : - MM_MODEM_CDMA_REGISTRATION_STATE_HOME); - evdo_set = TRUE; - } - if (get_roam_value (*iter, GENERIC_ROAM_TAG, &bool_val)) { - MMModemCdmaRegistrationState reg_state; - - reg_state = bool_val ? MM_MODEM_CDMA_REGISTRATION_STATE_ROAMING : - MM_MODEM_CDMA_REGISTRATION_STATE_HOME; - - mm_generic_cdma_query_reg_state_set_callback_1x_state (info, reg_state); - mm_generic_cdma_query_reg_state_set_callback_evdo_state (info, reg_state); - cdma_1x_set = TRUE; - evdo_set = TRUE; - } + get_roam_value (*iter, ROAM_1X_TAG, TRUE, &cdma1x_roam); + get_roam_value (*iter, ROAM_EVDO_TAG, TRUE, &evdo_roam); + if (get_roam_value (*iter, GENERIC_ROAM_TAG, FALSE, &bool_val)) + cdma1x_roam = evdo_roam = bool_val; /* Current system mode */ p = strstr (*iter, SYS_MODE_TAG); @@ -231,7 +240,8 @@ status_done (MMSerialPort *port, sys_mode = SYS_MODE_NO_SERVICE; else if (!strncmp (p, SYS_MODE_EVDO_TAG, strlen (SYS_MODE_EVDO_TAG))) sys_mode = SYS_MODE_EVDO_REV0; - else if (!strncmp (p, SYS_MODE_1X_TAG, strlen (SYS_MODE_1X_TAG))) + else if ( !strncmp (p, SYS_MODE_1X_TAG, strlen (SYS_MODE_1X_TAG)) + || !strncmp (p, SYS_MODE_CDMA_TAG, strlen (SYS_MODE_CDMA_TAG))) sys_mode = SYS_MODE_CDMA_1X; } @@ -259,24 +269,36 @@ status_done (MMSerialPort *port, } /* Update current system mode */ - if (sys_mode == SYS_MODE_EVDO_REV0 || sys_mode == SYS_MODE_EVDO_REVA) { + if (sys_mode_is_evdo (sys_mode)) { /* Prefer the explicit EVDO mode from EVDO_REV_TAG */ if (evdo_mode != SYS_MODE_UNKNOWN) sys_mode = evdo_mode; } priv->sys_mode = sys_mode; - if (registered || have_sid || sys_mode_has_service (sys_mode)) { - /* As a backup, if for some reason the registration states didn't get - * figured out by parsing the status info, set some generic registration - * states here. - */ - if (!cdma_1x_set) - mm_generic_cdma_query_reg_state_set_callback_1x_state (info, MM_MODEM_CDMA_REGISTRATION_STATE_REGISTERED); - - /* Ensure EVDO registration mode is set if we're at least in EVDO mode */ - if (!evdo_set && (sys_mode == SYS_MODE_EVDO_REV0 || sys_mode == SYS_MODE_EVDO_REVA)) - mm_generic_cdma_query_reg_state_set_callback_1x_state (info, MM_MODEM_CDMA_REGISTRATION_STATE_REGISTERED); + /* If the modem didn't report explicit registration with "Modem has + * registered" then get registration status by looking at either system + * mode or (for older devices that don't report that) just the SID. + */ + if (!registered) { + if (sys_mode != SYS_MODE_UNKNOWN) + registered = sys_mode_has_service (sys_mode); + else + registered = have_sid; + } + + if (registered) { + mm_generic_cdma_query_reg_state_set_callback_1x_state (info, + cdma1x_roam ? MM_MODEM_CDMA_REGISTRATION_STATE_ROAMING : + MM_MODEM_CDMA_REGISTRATION_STATE_HOME); + + if (sys_mode_is_evdo (sys_mode)) { + mm_generic_cdma_query_reg_state_set_callback_evdo_state (info, + evdo_roam ? MM_MODEM_CDMA_REGISTRATION_STATE_ROAMING : + MM_MODEM_CDMA_REGISTRATION_STATE_HOME); + } else { + mm_generic_cdma_query_reg_state_set_callback_evdo_state (info, MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN); + } } else { /* Not registered */ mm_generic_cdma_query_reg_state_set_callback_1x_state (info, MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN); @@ -289,35 +311,27 @@ done: static void query_registration_state (MMGenericCdma *cdma, + MMModemCdmaRegistrationState cur_cdma_state, + MMModemCdmaRegistrationState cur_evdo_state, MMModemCdmaRegistrationStateFn callback, gpointer user_data) { MMCallbackInfo *info; - MMSerialPort *primary, *secondary; - MMSerialPort *port; - - port = primary = mm_generic_cdma_get_port (cdma, MM_PORT_TYPE_PRIMARY); - secondary = mm_generic_cdma_get_port (cdma, MM_PORT_TYPE_SECONDARY); + MMAtSerialPort *port; - info = mm_generic_cdma_query_reg_state_callback_info_new (cdma, callback, user_data); - - if (mm_port_get_connected (MM_PORT (primary))) { - if (!secondary) { - info->error = g_error_new_literal (MM_MODEM_ERROR, MM_MODEM_ERROR_CONNECTED, - "Cannot get query registration state while connected"); - mm_callback_info_schedule (info); - return; - } + info = mm_generic_cdma_query_reg_state_callback_info_new (cdma, cur_cdma_state, cur_evdo_state, callback, user_data); - /* Use secondary port if primary is connected */ - port = secondary; + port = mm_generic_cdma_get_best_at_port (cdma, &info->error); + if (!port) { + mm_callback_info_schedule (info); + return; } - mm_serial_port_queue_command (port, "!STATUS", 3, status_done, info); + mm_at_serial_port_queue_command (port, "!STATUS", 3, status_done, info); } static void -pcstate_done (MMSerialPort *port, +pcstate_done (MMAtSerialPort *port, GString *response, GError *error, gpointer user_data) @@ -334,14 +348,14 @@ post_enable (MMGenericCdma *cdma, gpointer user_data) { MMCallbackInfo *info; - MMSerialPort *primary; + MMAtSerialPort *primary; info = mm_callback_info_new (MM_MODEM (cdma), callback, user_data); - primary = mm_generic_cdma_get_port (cdma, MM_PORT_TYPE_PRIMARY); + primary = mm_generic_cdma_get_at_port (cdma, MM_PORT_TYPE_PRIMARY); g_assert (primary); - mm_serial_port_queue_command (primary, "!pcstate=1", 5, pcstate_done, info); + mm_at_serial_port_queue_command (primary, "!pcstate=1", 5, pcstate_done, info); } static void @@ -350,14 +364,14 @@ post_disable (MMGenericCdma *cdma, gpointer user_data) { MMCallbackInfo *info; - MMSerialPort *primary; + MMAtSerialPort *primary; info = mm_callback_info_new (MM_MODEM (cdma), callback, user_data); - primary = mm_generic_cdma_get_port (cdma, MM_PORT_TYPE_PRIMARY); + primary = mm_generic_cdma_get_at_port (cdma, MM_PORT_TYPE_PRIMARY); g_assert (primary); - mm_serial_port_queue_command (primary, "!pcstate=0", 5, pcstate_done, info); + mm_at_serial_port_queue_command (primary, "!pcstate=0", 5, pcstate_done, info); } /*****************************************************************************/ diff --git a/plugins/mm-modem-sierra-gsm.c b/plugins/mm-modem-sierra-gsm.c index ee82234..bf5df31 100644 --- a/plugins/mm-modem-sierra-gsm.c +++ b/plugins/mm-modem-sierra-gsm.c @@ -11,9 +11,10 @@ * GNU General Public License for more details: * * Copyright (C) 2008 - 2009 Novell, Inc. - * Copyright (C) 2009 Red Hat, Inc. + * Copyright (C) 2009 - 2010 Red Hat, Inc. */ +#include <config.h> #include <stdlib.h> #include <stdio.h> #include <string.h> @@ -21,12 +22,18 @@ #include "mm-modem-sierra-gsm.h" #include "mm-errors.h" #include "mm-callback-info.h" +#include "mm-modem-helpers.h" static void modem_init (MMModem *modem_class); G_DEFINE_TYPE_EXTENDED (MMModemSierraGsm, mm_modem_sierra_gsm, MM_TYPE_GENERIC_GSM, 0, G_IMPLEMENT_INTERFACE (MM_TYPE_MODEM, modem_init)) +#define MM_MODEM_SIERRA_GSM_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), MM_TYPE_MODEM_SIERRA_GSM, MMModemSierraGsmPrivate)) + +typedef struct { + guint enable_wait_id; +} MMModemSierraGsmPrivate; MMModem * mm_modem_sierra_gsm_new (const char *device, @@ -45,54 +52,232 @@ mm_modem_sierra_gsm_new (const char *device, } /*****************************************************************************/ -/* Modem class override functions */ -/*****************************************************************************/ static void -pin_check_done (MMModem *modem, GError *error, gpointer user_data) +get_allowed_mode_done (MMAtSerialPort *port, + GString *response, + GError *error, + gpointer user_data) { MMCallbackInfo *info = (MMCallbackInfo *) user_data; + GRegex *r = NULL; + GMatchInfo *match_info; + + info->error = mm_modem_check_removed (info->modem, error); + if (info->error) + goto done; + + /* Example response: !SELRAT: 03, UMTS 3G Preferred */ + r = g_regex_new ("!SELRAT:\\s*(\\d+).*$", 0, 0, NULL); + if (!r) { + info->error = g_error_new_literal (MM_MODEM_ERROR, + MM_MODEM_ERROR_GENERAL, + "Failed to parse the allowed mode response"); + goto done; + } + + if (g_regex_match_full (r, response->str, response->len, 0, 0, &match_info, &info->error)) { + MMModemGsmAllowedMode mode = MM_MODEM_GSM_ALLOWED_MODE_ANY; + char *str; + + str = g_match_info_fetch (match_info, 1); + switch (atoi (str)) { + case 0: + mode = MM_MODEM_GSM_ALLOWED_MODE_ANY; + break; + case 1: + mode = MM_MODEM_GSM_ALLOWED_MODE_3G_ONLY; + break; + case 2: + mode = MM_MODEM_GSM_ALLOWED_MODE_2G_ONLY; + break; + case 3: + mode = MM_MODEM_GSM_ALLOWED_MODE_3G_PREFERRED; + break; + case 4: + mode = MM_MODEM_GSM_ALLOWED_MODE_2G_PREFERRED; + break; + default: + info->error = g_error_new (MM_MODEM_ERROR, + MM_MODEM_ERROR_GENERAL, + "Failed to parse the allowed mode response: '%s'", + response->str); + break; + } + g_free (str); + + mm_callback_info_set_result (info, GUINT_TO_POINTER (mode), NULL); + } - mm_generic_gsm_enable_complete (MM_GENERIC_GSM (modem), error, info); +done: + if (r) + g_regex_unref (r); + mm_callback_info_schedule (info); } -static gboolean -sierra_enabled (gpointer data) +static void +get_allowed_mode (MMGenericGsm *gsm, + MMModemUIntFn callback, + gpointer user_data) { - MMCallbackInfo *info = (MMCallbackInfo *) data; + MMCallbackInfo *info; + MMAtSerialPort *primary; - /* Now check the PIN explicitly, sierra doesn't seem to report - * that it needs it otherwise. - */ - mm_generic_gsm_check_pin (MM_GENERIC_GSM (info->modem), pin_check_done, info); - return FALSE; + info = mm_callback_info_uint_new (MM_MODEM (gsm), callback, user_data); + + /* Sierra secondary ports don't have full AT command interpreters */ + primary = mm_generic_gsm_get_at_port (gsm, MM_PORT_TYPE_PRIMARY); + if (!primary || mm_port_get_connected (MM_PORT (primary))) { + g_set_error_literal (&info->error, MM_MODEM_ERROR, MM_MODEM_ERROR_CONNECTED, + "Cannot perform this operation while connected"); + mm_callback_info_schedule (info); + return; + } + + mm_at_serial_port_queue_command (primary, "!SELRAT?", 3, get_allowed_mode_done, info); } static void -parent_enable_done (MMModem *modem, GError *error, gpointer user_data) +set_allowed_mode_done (MMAtSerialPort *port, + GString *response, + GError *error, + gpointer user_data) { MMCallbackInfo *info = (MMCallbackInfo *) user_data; - if (error) { - mm_generic_gsm_enable_complete (MM_GENERIC_GSM (modem), error, info); + if (error) + info->error = g_error_copy (error); + + mm_callback_info_schedule (info); +} + +static void +set_allowed_mode (MMGenericGsm *gsm, + MMModemGsmAllowedMode mode, + MMModemFn callback, + gpointer user_data) +{ + MMCallbackInfo *info; + MMAtSerialPort *primary; + char *command; + int idx = 0; + + info = mm_callback_info_new (MM_MODEM (gsm), callback, user_data); + + /* Sierra secondary ports don't have full AT command interpreters */ + primary = mm_generic_gsm_get_at_port (gsm, MM_PORT_TYPE_PRIMARY); + if (!primary || mm_port_get_connected (MM_PORT (primary))) { + g_set_error_literal (&info->error, MM_MODEM_ERROR, MM_MODEM_ERROR_CONNECTED, + "Cannot perform this operation while connected"); + mm_callback_info_schedule (info); return; } - /* Sierra returns OK on +CFUN=1 right away but needs some time - * to finish initialization. - */ - g_timeout_add_seconds (10, sierra_enabled, info); + switch (mode) { + case MM_MODEM_GSM_ALLOWED_MODE_2G_ONLY: + idx = 2; + break; + case MM_MODEM_GSM_ALLOWED_MODE_3G_ONLY: + idx = 1; + break; + case MM_MODEM_GSM_ALLOWED_MODE_2G_PREFERRED: + idx = 4; + break; + case MM_MODEM_GSM_ALLOWED_MODE_3G_PREFERRED: + idx = 3; + break; + case MM_MODEM_GSM_ALLOWED_MODE_ANY: + default: + break; + } + + command = g_strdup_printf ("!SELRAT=%d", idx); + mm_at_serial_port_queue_command (primary, command, 3, set_allowed_mode_done, info); + g_free (command); } static void -enable (MMModem *modem, MMModemFn callback, gpointer user_data) +get_act_request_done (MMAtSerialPort *port, + GString *response, + GError *error, + gpointer user_data) { - MMModem *parent_modem_iface; + MMCallbackInfo *info = user_data; + MMModemGsmAccessTech act = MM_MODEM_GSM_ACCESS_TECH_UNKNOWN; + const char *p; + + if (error) + info->error = g_error_copy (error); + else { + p = mm_strip_tag (response->str, "*CNTI:"); + p = strchr (p, ','); + if (p) + act = mm_gsm_string_to_access_tech (p + 1); + } + + mm_callback_info_set_result (info, GUINT_TO_POINTER (act), NULL); + mm_callback_info_schedule (info); +} + +static void +get_access_technology (MMGenericGsm *modem, + MMModemUIntFn callback, + gpointer user_data) +{ + MMAtSerialPort *port; MMCallbackInfo *info; - info = mm_callback_info_new (modem, callback, user_data); - parent_modem_iface = g_type_interface_peek_parent (MM_MODEM_GET_INTERFACE (modem)); - parent_modem_iface->enable (modem, parent_enable_done, info); + info = mm_callback_info_uint_new (MM_MODEM (modem), callback, user_data); + + port = mm_generic_gsm_get_best_at_port (modem, &info->error); + if (!port) { + mm_callback_info_schedule (info); + return; + } + + mm_at_serial_port_queue_command (port, "*CNTI=0", 3, get_act_request_done, info); +} + +/*****************************************************************************/ +/* Modem class override functions */ +/*****************************************************************************/ + +static gboolean +sierra_enabled (gpointer user_data) +{ + MMCallbackInfo *info = user_data; + MMGenericGsm *modem; + MMModemSierraGsmPrivate *priv; + + /* Make sure we don't use an invalid modem that may have been removed */ + if (info->modem) { + modem = MM_GENERIC_GSM (info->modem); + priv = MM_MODEM_SIERRA_GSM_GET_PRIVATE (modem); + priv->enable_wait_id = 0; + MM_GENERIC_GSM_CLASS (mm_modem_sierra_gsm_parent_class)->do_enable_power_up_done (modem, NULL, NULL, info); + } + return FALSE; +} + +static void +real_do_enable_power_up_done (MMGenericGsm *gsm, + GString *response, + GError *error, + MMCallbackInfo *info) +{ + MMModemSierraGsmPrivate *priv = MM_MODEM_SIERRA_GSM_GET_PRIVATE (gsm); + + if (error) { + /* Chain up to parent */ + MM_GENERIC_GSM_CLASS (mm_modem_sierra_gsm_parent_class)->do_enable_power_up_done (gsm, NULL, error, info); + return; + } + + /* Some Sierra devices return OK on +CFUN=1 right away but need some time + * to finish initialization. + */ + g_warn_if_fail (priv->enable_wait_id == 0); + priv->enable_wait_id = g_timeout_add_seconds (10, sierra_enabled, info); } static gboolean @@ -108,18 +293,25 @@ grab_port (MMModem *modem, MMPort *port; if (suggested_type == MM_PORT_TYPE_UNKNOWN) { - if (!mm_generic_gsm_get_port (gsm, MM_PORT_TYPE_PRIMARY)) + if (!mm_generic_gsm_get_at_port (gsm, MM_PORT_TYPE_PRIMARY)) ptype = MM_PORT_TYPE_PRIMARY; - else if (!mm_generic_gsm_get_port (gsm, MM_PORT_TYPE_SECONDARY)) + else if (!mm_generic_gsm_get_at_port (gsm, MM_PORT_TYPE_SECONDARY)) ptype = MM_PORT_TYPE_SECONDARY; } else ptype = suggested_type; port = mm_generic_gsm_grab_port (gsm, subsys, name, ptype, error); - if (port && MM_IS_SERIAL_PORT (port)) + if (port && MM_IS_AT_SERIAL_PORT (port)) { + GRegex *regex; + g_object_set (G_OBJECT (port), MM_PORT_CARRIER_DETECT, FALSE, NULL); + regex = g_regex_new ("\\r\\n\\+PACSP0\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); + mm_at_serial_port_add_unsolicited_msg_handler (MM_AT_SERIAL_PORT (port), regex, NULL, NULL, NULL); + g_regex_unref (regex); + } + return !!port; } @@ -128,7 +320,6 @@ grab_port (MMModem *modem, static void modem_init (MMModem *modem_class) { - modem_class->enable = enable; modem_class->grab_port = grab_port; } @@ -138,8 +329,27 @@ mm_modem_sierra_gsm_init (MMModemSierraGsm *self) } static void +dispose (GObject *object) +{ + MMModemSierraGsmPrivate *priv = MM_MODEM_SIERRA_GSM_GET_PRIVATE (object); + + if (priv->enable_wait_id) + g_source_remove (priv->enable_wait_id); +} + +static void mm_modem_sierra_gsm_class_init (MMModemSierraGsmClass *klass) { + GObjectClass *object_class = G_OBJECT_CLASS (klass); + MMGenericGsmClass *gsm_class = MM_GENERIC_GSM_CLASS (klass); + mm_modem_sierra_gsm_parent_class = g_type_class_peek_parent (klass); + g_type_class_add_private (object_class, sizeof (MMModemSierraGsmPrivate)); + + object_class->dispose = dispose; + gsm_class->do_enable_power_up_done = real_do_enable_power_up_done; + gsm_class->set_allowed_mode = set_allowed_mode; + gsm_class->get_allowed_mode = get_allowed_mode; + gsm_class->get_access_technology = get_access_technology; } diff --git a/plugins/mm-modem-simtech-gsm.c b/plugins/mm-modem-simtech-gsm.c new file mode 100644 index 0000000..07820b3 --- /dev/null +++ b/plugins/mm-modem-simtech-gsm.c @@ -0,0 +1,471 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details: + * + * Copyright (C) 2008 - 2009 Novell, Inc. + * Copyright (C) 2009 - 2010 Red Hat, Inc. + */ + +#include <config.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> +#include <ctype.h> +#include "mm-modem-simtech-gsm.h" +#include "mm-at-serial-port.h" +#include "mm-errors.h" +#include "mm-callback-info.h" +#include "mm-modem-helpers.h" + +static void modem_init (MMModem *modem_class); + +G_DEFINE_TYPE_EXTENDED (MMModemSimtechGsm, mm_modem_simtech_gsm, MM_TYPE_GENERIC_GSM, 0, + G_IMPLEMENT_INTERFACE (MM_TYPE_MODEM, modem_init)) + +MMModem * +mm_modem_simtech_gsm_new (const char *device, + const char *driver, + const char *plugin) +{ + g_return_val_if_fail (device != NULL, NULL); + g_return_val_if_fail (driver != NULL, NULL); + g_return_val_if_fail (plugin != NULL, NULL); + + return MM_MODEM (g_object_new (MM_TYPE_MODEM_SIMTECH_GSM, + MM_MODEM_MASTER_DEVICE, device, + MM_MODEM_DRIVER, driver, + MM_MODEM_PLUGIN, plugin, + NULL)); +} + +/*****************************************************************************/ + +#define ACQ_ORDER_TAG "acq-order" + +static void +get_mode_pref_done (MMAtSerialPort *port, + GString *response, + GError *error, + gpointer user_data) +{ + MMCallbackInfo *info = (MMCallbackInfo *) user_data; + const char *p; + gint modepref = -1; + guint32 acqord; + MMModemGsmAllowedMode allowed = MM_MODEM_GSM_ALLOWED_MODE_ANY; + + info->error = mm_modem_check_removed (info->modem, error); + if (info->error) + goto done; + + p = mm_strip_tag (response->str, "+CNMP:"); + if (!p) { + info->error = g_error_new_literal (MM_MODEM_ERROR, + MM_MODEM_ERROR_GENERAL, + "Failed to parse the mode preference response"); + goto done; + } + + acqord = GPOINTER_TO_UINT (mm_callback_info_get_data (info, ACQ_ORDER_TAG)); + modepref = atoi (p); + + if (modepref == 2) { + /* Automatic */ + if (acqord == 0) + allowed = MM_MODEM_GSM_ALLOWED_MODE_ANY; + else if (acqord == 1) + allowed = MM_MODEM_GSM_ALLOWED_MODE_2G_PREFERRED; + else if (acqord == 2) + allowed = MM_MODEM_GSM_ALLOWED_MODE_3G_PREFERRED; + else { + info->error = g_error_new (MM_MODEM_ERROR, + MM_MODEM_ERROR_GENERAL, + "Unknown acqisition order preference %d", + acqord); + } + } else if (modepref == 13) { + /* GSM only */ + allowed = MM_MODEM_GSM_ALLOWED_MODE_2G_ONLY; + } else if (modepref == 14) { + /* WCDMA only */ + allowed = MM_MODEM_GSM_ALLOWED_MODE_3G_ONLY; + } else { + info->error = g_error_new (MM_MODEM_ERROR, + MM_MODEM_ERROR_GENERAL, + "Unknown mode preference %d", + modepref); + } + +done: + if (!info->error) + mm_callback_info_set_result (info, GUINT_TO_POINTER (allowed), NULL); + mm_callback_info_schedule (info); +} + +static void +get_acq_order_done (MMAtSerialPort *port, + GString *response, + GError *error, + gpointer user_data) +{ + MMCallbackInfo *info = (MMCallbackInfo *) user_data; + const char *p; + gint acqord = -1; + + info->error = mm_modem_check_removed (info->modem, error); + if (info->error) + goto done; + + p = mm_strip_tag (response->str, "+CNAOP:"); + if (!p) { + info->error = g_error_new_literal (MM_MODEM_ERROR, + MM_MODEM_ERROR_GENERAL, + "Failed to parse the acqisition order response"); + goto done; + } + + acqord = atoi (p); + if (acqord < 0 || acqord > 2) { + info->error = g_error_new (MM_MODEM_ERROR, + MM_MODEM_ERROR_GENERAL, + "Unknown acquisition order response %d", + acqord); + } else { + /* Cache the acquisition preference */ + mm_callback_info_set_data (info, ACQ_ORDER_TAG, GUINT_TO_POINTER (acqord), NULL); + } + +done: + if (info->error) + mm_callback_info_schedule (info); + else + mm_at_serial_port_queue_command (port, "+CNMP?", 3, get_mode_pref_done, info); +} + +static void +get_allowed_mode (MMGenericGsm *gsm, + MMModemUIntFn callback, + gpointer user_data) +{ + MMCallbackInfo *info; + MMAtSerialPort *port; + + info = mm_callback_info_uint_new (MM_MODEM (gsm), callback, user_data); + + port = mm_generic_gsm_get_best_at_port (gsm, &info->error); + if (!port) { + mm_callback_info_schedule (info); + return; + } + + mm_at_serial_port_queue_command (port, "+CNAOP?", 3, get_acq_order_done, info); +} + +static void +set_acq_order_done (MMAtSerialPort *port, + GString *response, + GError *error, + gpointer user_data) +{ + MMCallbackInfo *info = (MMCallbackInfo *) user_data; + + if (error) + info->error = g_error_copy (error); + + mm_callback_info_schedule (info); +} + +static void +set_mode_pref_done (MMAtSerialPort *port, + GString *response, + GError *error, + gpointer user_data) +{ + MMCallbackInfo *info = (MMCallbackInfo *) user_data; + guint32 naop; + char *command; + + info->error = mm_modem_check_removed (info->modem, error); + if (info->error) { + mm_callback_info_schedule (info); + return; + } + + naop = GPOINTER_TO_UINT (mm_callback_info_get_data (info, ACQ_ORDER_TAG)); + command = g_strdup_printf ("+CNAOP=%u", naop); + mm_at_serial_port_queue_command (port, command, 3, set_acq_order_done, info); + g_free (command); +} + +static void +set_allowed_mode (MMGenericGsm *gsm, + MMModemGsmAllowedMode mode, + MMModemFn callback, + gpointer user_data) +{ + MMCallbackInfo *info; + MMAtSerialPort *port; + char *command; + guint32 nmp = 2; /* automatic mode preference */ + guint32 naop = 0; /* automatic acquisition order */ + + info = mm_callback_info_new (MM_MODEM (gsm), callback, user_data); + + port = mm_generic_gsm_get_best_at_port (gsm, &info->error); + if (!port) { + mm_callback_info_schedule (info); + return; + } + + switch (mode) { + case MM_MODEM_GSM_ALLOWED_MODE_2G_ONLY: + nmp = 13; + break; + case MM_MODEM_GSM_ALLOWED_MODE_3G_ONLY: + nmp = 14; + break; + case MM_MODEM_GSM_ALLOWED_MODE_3G_PREFERRED: + naop = 2; + break; + case MM_MODEM_GSM_ALLOWED_MODE_2G_PREFERRED: + naop = 3; + break; + case MM_MODEM_GSM_ALLOWED_MODE_ANY: + default: + break; + } + + mm_callback_info_set_data (info, ACQ_ORDER_TAG, GUINT_TO_POINTER (naop), NULL); + + command = g_strdup_printf ("+CNMP=%u", nmp); + mm_at_serial_port_queue_command (port, command, 3, set_mode_pref_done, info); + g_free (command); +} + +static MMModemGsmAccessTech +simtech_act_to_mm_act (int nsmod) +{ + if (nsmod == 1) + return MM_MODEM_GSM_ACCESS_TECH_GSM; + else if (nsmod == 2) + return MM_MODEM_GSM_ACCESS_TECH_GPRS; + else if (nsmod == 3) + return MM_MODEM_GSM_ACCESS_TECH_EDGE; + else if (nsmod == 4) + return MM_MODEM_GSM_ACCESS_TECH_UMTS; + else if (nsmod == 5) + return MM_MODEM_GSM_ACCESS_TECH_HSDPA; + else if (nsmod == 6) + return MM_MODEM_GSM_ACCESS_TECH_HSUPA; + else if (nsmod == 7) + return MM_MODEM_GSM_ACCESS_TECH_HSPA; + + return MM_MODEM_GSM_ACCESS_TECH_UNKNOWN; +} + +static void +get_act_tech_done (MMAtSerialPort *port, + GString *response, + GError *error, + gpointer user_data) +{ + MMCallbackInfo *info = user_data; + MMModemGsmAccessTech act = MM_MODEM_GSM_ACCESS_TECH_UNKNOWN; + const char *p; + + info->error = mm_modem_check_removed (info->modem, error); + if (info->error) { + mm_callback_info_schedule (info); + return; + } + + p = mm_strip_tag (response->str, "+CNSMOD:"); + if (p) + p = strchr (p, ','); + + if (!p || !isdigit (*(p + 1))) { + info->error = g_error_new_literal (MM_MODEM_ERROR, + MM_MODEM_ERROR_GENERAL, + "Failed to parse the access technology response"); + } else { + act = simtech_act_to_mm_act (atoi (p + 1)); + mm_callback_info_set_result (info, GUINT_TO_POINTER (act), NULL); + } + + mm_callback_info_schedule (info); +} + +static void +get_access_technology (MMGenericGsm *modem, + MMModemUIntFn callback, + gpointer user_data) +{ + MMAtSerialPort *port; + MMCallbackInfo *info; + + info = mm_callback_info_uint_new (MM_MODEM (modem), callback, user_data); + + port = mm_generic_gsm_get_best_at_port (modem, &info->error); + if (!port) { + mm_callback_info_schedule (info); + return; + } + + mm_at_serial_port_queue_command (port, "AT+CNSMOD?", 3, get_act_tech_done, info); +} + +static void +handle_act_change (MMAtSerialPort *port, + GMatchInfo *match_info, + gpointer user_data) +{ + MMModemSimtechGsm *self = MM_MODEM_SIMTECH_GSM (user_data); + MMModemGsmAccessTech act; + char *str; + + str = g_match_info_fetch (match_info, 1); + if (str && strlen (str)) { + act = simtech_act_to_mm_act (atoi (str)); + mm_generic_gsm_update_access_technology (MM_GENERIC_GSM (self), act); + } + g_free (str); +} + +/*****************************************************************************/ + +static void +real_do_enable_power_up_done (MMGenericGsm *gsm, + GString *response, + GError *error, + MMCallbackInfo *info) +{ + if (!error) { + MMAtSerialPort *primary; + + /* Enable unsolicited result codes */ + primary = mm_generic_gsm_get_at_port (gsm, MM_PORT_TYPE_PRIMARY); + g_assert (primary); + + /* Autoreport access technology changes */ + mm_at_serial_port_queue_command (primary, "+CNSMOD=1", 5, NULL, NULL); + + /* Autoreport CSQ (first arg), and only report when it changes (second arg) */ + mm_at_serial_port_queue_command (primary, "+AUTOCSQ=1,1", 5, NULL, NULL); + } + + /* Chain up to parent */ + MM_GENERIC_GSM_CLASS (mm_modem_simtech_gsm_parent_class)->do_enable_power_up_done (gsm, NULL, error, info); +} + +/*****************************************************************************/ + +typedef struct { + MMModem *modem; + MMModemFn callback; + gpointer user_data; +} DisableInfo; + +static void +disable_unsolicited_done (MMAtSerialPort *port, + GString *response, + GError *error, + gpointer user_data) + +{ + MMModem *parent_modem_iface; + DisableInfo *info = user_data; + + parent_modem_iface = g_type_interface_peek_parent (MM_MODEM_GET_INTERFACE (info->modem)); + parent_modem_iface->disable (info->modem, info->callback, info->user_data); + g_free (info); +} + +static void +disable (MMModem *modem, + MMModemFn callback, + gpointer user_data) +{ + MMAtSerialPort *primary; + DisableInfo *info; + + info = g_malloc0 (sizeof (DisableInfo)); + info->callback = callback; + info->user_data = user_data; + info->modem = modem; + + primary = mm_generic_gsm_get_at_port (MM_GENERIC_GSM (modem), MM_PORT_TYPE_PRIMARY); + g_assert (primary); + + /* Turn off unsolicited responses */ + mm_at_serial_port_queue_command (primary, "+CNSMOD=0;+AUTOCSQ=0", 5, disable_unsolicited_done, info); +} + +static gboolean +grab_port (MMModem *modem, + const char *subsys, + const char *name, + MMPortType suggested_type, + gpointer user_data, + GError **error) +{ + MMGenericGsm *gsm = MM_GENERIC_GSM (modem); + MMPortType ptype = MM_PORT_TYPE_IGNORED; + MMPort *port; + + if (suggested_type == MM_PORT_TYPE_UNKNOWN) { + if (!mm_generic_gsm_get_at_port (gsm, MM_PORT_TYPE_PRIMARY)) + ptype = MM_PORT_TYPE_PRIMARY; + else if (!mm_generic_gsm_get_at_port (gsm, MM_PORT_TYPE_SECONDARY)) + ptype = MM_PORT_TYPE_SECONDARY; + } else + ptype = suggested_type; + + port = mm_generic_gsm_grab_port (gsm, subsys, name, ptype, error); + + if (port && MM_IS_AT_SERIAL_PORT (port)) { + GRegex *regex; + + regex = g_regex_new ("\\r\\n\\+CNSMOD:\\s*(\\d)\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); + mm_at_serial_port_add_unsolicited_msg_handler (MM_AT_SERIAL_PORT (port), regex, handle_act_change, modem, NULL); + g_regex_unref (regex); + } + + return !!port; +} + +/*****************************************************************************/ + +static void +modem_init (MMModem *modem_class) +{ + modem_class->disable = disable; + modem_class->grab_port = grab_port; +} + +static void +mm_modem_simtech_gsm_init (MMModemSimtechGsm *self) +{ +} + +static void +mm_modem_simtech_gsm_class_init (MMModemSimtechGsmClass *klass) +{ + MMGenericGsmClass *gsm_class = MM_GENERIC_GSM_CLASS (klass); + + mm_modem_simtech_gsm_parent_class = g_type_class_peek_parent (klass); + + gsm_class->do_enable_power_up_done = real_do_enable_power_up_done; + gsm_class->set_allowed_mode = set_allowed_mode; + gsm_class->get_allowed_mode = get_allowed_mode; + gsm_class->get_access_technology = get_access_technology; +} + diff --git a/plugins/mm-modem-simtech-gsm.h b/plugins/mm-modem-simtech-gsm.h new file mode 100644 index 0000000..0ba3c43 --- /dev/null +++ b/plugins/mm-modem-simtech-gsm.h @@ -0,0 +1,43 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details: + * + * Copyright (C) 2008 - 2009 Novell, Inc. + * Copyright (C) 2009 - 2010 Red Hat, Inc. + */ + +#ifndef MM_MODEM_SIMTECH_GSM_H +#define MM_MODEM_SIMTECH_GSM_H + +#include "mm-generic-gsm.h" + +#define MM_TYPE_MODEM_SIMTECH_GSM (mm_modem_simtech_gsm_get_type ()) +#define MM_MODEM_SIMTECH_GSM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_MODEM_SIMTECH_GSM, MMModemSimtechGsm)) +#define MM_MODEM_SIMTECH_GSM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_MODEM_SIMTECH_GSM, MMModemSimtechGsmClass)) +#define MM_IS_MODEM_SIMTECH_GSM(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_MODEM_SIMTECH_GSM)) +#define MM_IS_MODEM_SIMTECH_GSM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_MODEM_SIMTECH_GSM)) +#define MM_MODEM_SIMTECH_GSM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_MODEM_SIMTECH_GSM, MMModemSimtechGsmClass)) + +typedef struct { + MMGenericGsm parent; +} MMModemSimtechGsm; + +typedef struct { + MMGenericGsmClass parent; +} MMModemSimtechGsmClass; + +GType mm_modem_simtech_gsm_get_type (void); + +MMModem *mm_modem_simtech_gsm_new (const char *device, + const char *driver, + const char *plugin); + +#endif /* MM_MODEM_SIMTECH_H */ diff --git a/plugins/mm-modem-zte.c b/plugins/mm-modem-zte.c index 92c23ae..ba8a1db 100644 --- a/plugins/mm-modem-zte.c +++ b/plugins/mm-modem-zte.c @@ -11,9 +11,10 @@ * GNU General Public License for more details: * * Copyright (C) 2008 - 2009 Novell, Inc. - * Copyright (C) 2009 Red Hat, Inc. + * Copyright (C) 2009 - 2010 Red Hat, Inc. */ +#include <config.h> #include <stdlib.h> #include <stdio.h> #include <string.h> @@ -22,6 +23,7 @@ #include "mm-serial-port.h" #include "mm-errors.h" #include "mm-callback-info.h" +#include "mm-modem-helpers.h" static void modem_init (MMModem *modem_class); @@ -32,6 +34,8 @@ G_DEFINE_TYPE_EXTENDED (MMModemZte, mm_modem_zte, MM_TYPE_GENERIC_GSM, 0, typedef struct { gboolean init_retried; + guint32 cpms_tries; + guint cpms_timeout; } MMModemZtePrivate; MMModem * @@ -51,35 +55,278 @@ mm_modem_zte_new (const char *device, } /*****************************************************************************/ -/* Modem class override functions */ + +static void +zte_access_tech_changed (MMAtSerialPort *port, + GMatchInfo *info, + gpointer user_data) +{ + MMModemGsmAccessTech act = MM_MODEM_GSM_ACCESS_TECH_UNKNOWN; + char *str; + + str = g_match_info_fetch (info, 1); + if (str) + act = mm_gsm_string_to_access_tech (str); + g_free (str); + + mm_generic_gsm_update_access_technology (MM_GENERIC_GSM (user_data), act); +} + /*****************************************************************************/ static void -init_modem_done (MMSerialPort *port, - GString *response, - GError *error, - gpointer user_data) +get_allowed_mode_done (MMAtSerialPort *port, + GString *response, + GError *error, + gpointer user_data) { MMCallbackInfo *info = (MMCallbackInfo *) user_data; + GRegex *r = NULL; + GMatchInfo *match_info; + + info->error = mm_modem_check_removed (info->modem, error); + if (info->error) + goto done; + + r = g_regex_new ("+ZSNT:\\s*(\\d),(\\d),(\\d)", G_REGEX_UNGREEDY, 0, NULL); + if (!r) { + info->error = g_error_new_literal (MM_MODEM_ERROR, + MM_MODEM_ERROR_GENERAL, + "Failed to parse the allowed mode response"); + goto done; + } - mm_generic_gsm_enable_complete (MM_GENERIC_GSM (info->modem), error, info); + if (g_regex_match_full (r, response->str, response->len, 0, 0, &match_info, &info->error)) { + MMModemGsmAllowedMode mode = MM_MODEM_GSM_ALLOWED_MODE_ANY; + char *str; + int cm_mode = -1, pref_acq = -1; + + str = g_match_info_fetch (match_info, 1); + cm_mode = atoi (str); + g_free (str); + + str = g_match_info_fetch (match_info, 3); + pref_acq = atoi (str); + g_free (str); + + g_match_info_free (match_info); + + if (cm_mode < 0 || cm_mode > 2 || pref_acq < 0 || pref_acq > 2) { + info->error = g_error_new (MM_MODEM_ERROR, + MM_MODEM_ERROR_GENERAL, + "Failed to parse the allowed mode response: '%s'", + response->str); + goto done; + } + + if (cm_mode == 0) { /* Both 2G and 3G allowed */ + if (pref_acq == 0) + mode = MM_MODEM_GSM_ALLOWED_MODE_ANY; + else if (pref_acq == 1) + mode = MM_MODEM_GSM_ALLOWED_MODE_2G_PREFERRED; + else if (pref_acq == 2) + mode = MM_MODEM_GSM_ALLOWED_MODE_3G_PREFERRED; + } else if (cm_mode == 1) /* GSM only */ + mode = MM_MODEM_GSM_ALLOWED_MODE_2G_ONLY; + else if (cm_mode == 2) /* WCDMA only */ + mode = MM_MODEM_GSM_ALLOWED_MODE_3G_ONLY; + + mm_callback_info_set_result (info, GUINT_TO_POINTER (mode), NULL); + } + +done: + if (r) + g_regex_unref (r); + mm_callback_info_schedule (info); +} + +static void +get_allowed_mode (MMGenericGsm *gsm, + MMModemUIntFn callback, + gpointer user_data) +{ + MMCallbackInfo *info; + MMAtSerialPort *port; + + info = mm_callback_info_uint_new (MM_MODEM (gsm), callback, user_data); + + port = mm_generic_gsm_get_best_at_port (gsm, &info->error); + if (!port) { + mm_callback_info_schedule (info); + return; + } + + mm_at_serial_port_queue_command (port, "AT+ZSNT?", 3, get_allowed_mode_done, info); } static void -pin_check_done (MMModem *modem, GError *error, gpointer user_data) +set_allowed_mode_done (MMAtSerialPort *port, + GString *response, + GError *error, + gpointer user_data) { MMCallbackInfo *info = (MMCallbackInfo *) user_data; - MMSerialPort *primary; - if (error) { - mm_generic_gsm_enable_complete (MM_GENERIC_GSM (modem), error, info); + if (error) + info->error = g_error_copy (error); + + mm_callback_info_schedule (info); +} + +static void +set_allowed_mode (MMGenericGsm *gsm, + MMModemGsmAllowedMode mode, + MMModemFn callback, + gpointer user_data) +{ + MMCallbackInfo *info; + MMAtSerialPort *port; + char *command; + int cm_mode = 0, pref_acq = 0; + + info = mm_callback_info_new (MM_MODEM (gsm), callback, user_data); + + port = mm_generic_gsm_get_best_at_port (gsm, &info->error); + if (!port) { + mm_callback_info_schedule (info); return; } - /* Finish the initialization */ - primary = mm_generic_gsm_get_port (MM_GENERIC_GSM (modem), MM_PORT_TYPE_PRIMARY); - g_assert (primary); - mm_serial_port_queue_command (primary, "Z E0 V1 X4 &C1 +CMEE=1;+CFUN=1;", 10, init_modem_done, info); + switch (mode) { + case MM_MODEM_GSM_ALLOWED_MODE_2G_ONLY: + cm_mode = 1; + pref_acq = 0; + break; + case MM_MODEM_GSM_ALLOWED_MODE_3G_ONLY: + cm_mode = 2; + pref_acq = 0; + break; + case MM_MODEM_GSM_ALLOWED_MODE_2G_PREFERRED: + cm_mode = 0; + pref_acq = 1; + break; + case MM_MODEM_GSM_ALLOWED_MODE_3G_PREFERRED: + cm_mode = 0; + pref_acq = 2; + break; + case MM_MODEM_GSM_ALLOWED_MODE_ANY: + default: + break; + } + + command = g_strdup_printf ("AT+ZSNT=%d,0,%d", cm_mode, pref_acq); + mm_at_serial_port_queue_command (port, command, 3, set_allowed_mode_done, info); + g_free (command); +} + +static void +get_act_request_done (MMAtSerialPort *port, + GString *response, + GError *error, + gpointer user_data) +{ + MMCallbackInfo *info = user_data; + MMModemGsmAccessTech act = MM_MODEM_GSM_ACCESS_TECH_UNKNOWN; + const char *p; + + if (error) + info->error = g_error_copy (error); + else { + /* Sample response from an MF626: + * +ZPAS: "GPRS/EDGE","CS_ONLY" + */ + p = mm_strip_tag (response->str, "+ZPAS:"); + act = mm_gsm_string_to_access_tech (p); + } + + mm_callback_info_set_result (info, GUINT_TO_POINTER (act), NULL); + mm_callback_info_schedule (info); +} + +static void +get_access_technology (MMGenericGsm *modem, + MMModemUIntFn callback, + gpointer user_data) +{ + MMAtSerialPort *port; + MMCallbackInfo *info; + + info = mm_callback_info_uint_new (MM_MODEM (modem), callback, user_data); + + port = mm_generic_gsm_get_best_at_port (modem, &info->error); + if (!port) { + mm_callback_info_schedule (info); + return; + } + + mm_at_serial_port_queue_command (port, "+ZPAS?", 3, get_act_request_done, info); +} + +/*****************************************************************************/ +/* Modem class override functions */ +/*****************************************************************************/ + +static void cpms_try_done (MMAtSerialPort *port, + GString *response, + GError *error, + gpointer user_data); + +static gboolean +cpms_timeout_cb (gpointer user_data) +{ + MMCallbackInfo *info = user_data; + MMModem *modem = info->modem; + MMAtSerialPort *primary; + + if (modem) { + MM_MODEM_ZTE_GET_PRIVATE (modem)->cpms_timeout = 0; + primary = mm_generic_gsm_get_at_port (MM_GENERIC_GSM (modem), MM_PORT_TYPE_PRIMARY); + g_assert (primary); + mm_at_serial_port_queue_command (primary, "+CPMS?", 10, cpms_try_done, info); + } + return FALSE; +} + +static void +cpms_try_done (MMAtSerialPort *port, + GString *response, + GError *error, + gpointer user_data) +{ + MMCallbackInfo *info = user_data; + MMModemZtePrivate *priv = MM_MODEM_ZTE_GET_PRIVATE (info->modem); + + if (error && g_error_matches (error, MM_MOBILE_ERROR, MM_MOBILE_ERROR_SIM_BUSY)) { + if (priv->cpms_tries++ < 4) { + if (priv->cpms_timeout) + g_source_remove (priv->cpms_timeout); + + /* Have to try a few times; sometimes the SIM is busy */ + priv->cpms_timeout = g_timeout_add_seconds (2, cpms_timeout_cb, info); + return; + } else { + /* oh well, proceed... */ + error = NULL; + } + } + + mm_generic_gsm_enable_complete (MM_GENERIC_GSM (info->modem), error, info); +} + +static void +init_modem_done (MMAtSerialPort *port, + GString *response, + GError *error, + gpointer user_data) +{ + MMCallbackInfo *info = (MMCallbackInfo *) user_data; + + /* Attempt to disable floods of "+ZUSIMR:2" unsolicited responses that + * eventually fill up the device's buffers and make it crash. Normally + * done during probing, but if the device has a PIN enabled it won't + * accept the +CPMS? during the probe and we have to do it here. + */ + mm_at_serial_port_queue_command (port, "+CPMS?", 10, cpms_try_done, info); } static void enable_flash_done (MMSerialPort *port, @@ -87,7 +334,7 @@ static void enable_flash_done (MMSerialPort *port, gpointer user_data); static void -pre_init_done (MMSerialPort *port, +pre_init_done (MMAtSerialPort *port, GString *response, GError *error, gpointer user_data) @@ -98,15 +345,14 @@ pre_init_done (MMSerialPort *port, if (error) { /* Retry the init string one more time; the modem sometimes throws it away */ if ( !priv->init_retried - && g_error_matches (error, MM_SERIAL_ERROR, MM_SERIAL_RESPONSE_TIMEOUT)) { + && g_error_matches (error, MM_SERIAL_ERROR, MM_SERIAL_ERROR_RESPONSE_TIMEOUT)) { priv->init_retried = TRUE; - enable_flash_done (port, NULL, user_data); + enable_flash_done (MM_SERIAL_PORT (port), NULL, user_data); } else mm_generic_gsm_enable_complete (MM_GENERIC_GSM (info->modem), error, info); } else { - /* Now check the PIN explicitly, zte doesn't seem to report - that it needs it otherwise */ - mm_generic_gsm_check_pin (MM_GENERIC_GSM (info->modem), pin_check_done, info); + /* Finish the initialization */ + mm_at_serial_port_queue_command (port, "Z E0 V1 X4 &C1 +CMEE=1;+CFUN=1;", 10, init_modem_done, info); } } @@ -118,7 +364,7 @@ enable_flash_done (MMSerialPort *port, GError *error, gpointer user_data) if (error) mm_generic_gsm_enable_complete (MM_GENERIC_GSM (info->modem), error, info); else - mm_serial_port_queue_command (port, "E0 V1", 3, pre_init_done, user_data); + mm_at_serial_port_queue_command (MM_AT_SERIAL_PORT (port), "E0 V1", 3, pre_init_done, user_data); } static void @@ -126,15 +372,15 @@ do_enable (MMGenericGsm *modem, MMModemFn callback, gpointer user_data) { MMModemZtePrivate *priv = MM_MODEM_ZTE_GET_PRIVATE (modem); MMCallbackInfo *info; - MMSerialPort *primary; + MMAtSerialPort *primary; priv->init_retried = FALSE; - primary = mm_generic_gsm_get_port (modem, MM_PORT_TYPE_PRIMARY); + primary = mm_generic_gsm_get_at_port (modem, MM_PORT_TYPE_PRIMARY); g_assert (primary); info = mm_callback_info_new (MM_MODEM (modem), callback, user_data); - mm_serial_port_flash (primary, 100, enable_flash_done, info); + mm_serial_port_flash (MM_SERIAL_PORT (primary), 100, FALSE, enable_flash_done, info); } static void @@ -165,42 +411,41 @@ grab_port (MMModem *modem, MMPort *port = NULL; if (suggested_type == MM_PORT_TYPE_UNKNOWN) { - if (!mm_generic_gsm_get_port (gsm, MM_PORT_TYPE_PRIMARY)) + if (!mm_generic_gsm_get_at_port (gsm, MM_PORT_TYPE_PRIMARY)) ptype = MM_PORT_TYPE_PRIMARY; - else if (!mm_generic_gsm_get_port (gsm, MM_PORT_TYPE_SECONDARY)) + else if (!mm_generic_gsm_get_at_port (gsm, MM_PORT_TYPE_SECONDARY)) ptype = MM_PORT_TYPE_SECONDARY; } else ptype = suggested_type; port = mm_generic_gsm_grab_port (gsm, subsys, name, ptype, error); - if (port && MM_IS_SERIAL_PORT (port)) { + if (port && MM_IS_AT_SERIAL_PORT (port)) { GRegex *regex; - mm_generic_gsm_set_unsolicited_registration (gsm, TRUE); g_object_set (port, MM_PORT_CARRIER_DETECT, FALSE, NULL); regex = g_regex_new ("\\r\\n\\+ZUSIMR:(.*)\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); - mm_serial_port_add_unsolicited_msg_handler (MM_SERIAL_PORT (port), regex, NULL, NULL, NULL); + mm_at_serial_port_add_unsolicited_msg_handler (MM_AT_SERIAL_PORT (port), regex, NULL, NULL, NULL); g_regex_unref (regex); /* Unsolicted operator display */ regex = g_regex_new ("\\r\\n\\+ZDONR: (.*)\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); - mm_serial_port_add_unsolicited_msg_handler (MM_SERIAL_PORT (port), regex, NULL, NULL, NULL); + mm_at_serial_port_add_unsolicited_msg_handler (MM_AT_SERIAL_PORT (port), regex, NULL, NULL, NULL); g_regex_unref (regex); /* Current network and service domain */ - regex = g_regex_new ("\\r\\n\\+ZPASR: (.*)\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); - mm_serial_port_add_unsolicited_msg_handler (MM_SERIAL_PORT (port), regex, NULL, NULL, NULL); + regex = g_regex_new ("\\r\\n\\+ZPASR:\\s*(.*)\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); + mm_at_serial_port_add_unsolicited_msg_handler (MM_AT_SERIAL_PORT (port), regex, zte_access_tech_changed, modem, NULL); g_regex_unref (regex); /* SIM request to Build Main Menu */ regex = g_regex_new ("\\r\\n\\+ZPSTM: (.*)\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); - mm_serial_port_add_unsolicited_msg_handler (MM_SERIAL_PORT (port), regex, NULL, NULL, NULL); + mm_at_serial_port_add_unsolicited_msg_handler (MM_AT_SERIAL_PORT (port), regex, NULL, NULL, NULL); g_regex_unref (regex); /* SIM request to Rebuild Main Menu */ regex = g_regex_new ("\\r\\n\\+ZEND\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); - mm_serial_port_add_unsolicited_msg_handler (MM_SERIAL_PORT (port), regex, NULL, NULL, NULL); + mm_at_serial_port_add_unsolicited_msg_handler (MM_AT_SERIAL_PORT (port), regex, NULL, NULL, NULL); g_regex_unref (regex); } @@ -222,6 +467,16 @@ mm_modem_zte_init (MMModemZte *self) } static void +dispose (GObject *object) +{ + MMModemZte *self = MM_MODEM_ZTE (object); + MMModemZtePrivate *priv = MM_MODEM_ZTE_GET_PRIVATE (self); + + if (priv->cpms_timeout) + g_source_remove (priv->cpms_timeout); +} + +static void mm_modem_zte_class_init (MMModemZteClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); @@ -230,6 +485,10 @@ mm_modem_zte_class_init (MMModemZteClass *klass) mm_modem_zte_parent_class = g_type_class_peek_parent (klass); g_type_class_add_private (object_class, sizeof (MMModemZtePrivate)); + object_class->dispose = dispose; gsm_class->do_enable = do_enable; + gsm_class->set_allowed_mode = set_allowed_mode; + gsm_class->get_allowed_mode = get_allowed_mode; + gsm_class->get_access_technology = get_access_technology; } diff --git a/plugins/mm-plugin-anydata.c b/plugins/mm-plugin-anydata.c index e451714..94f4f10 100644 --- a/plugins/mm-plugin-anydata.c +++ b/plugins/mm-plugin-anydata.c @@ -42,9 +42,11 @@ mm_plugin_create (void) static guint32 get_level_for_capabilities (guint32 capabilities) { - /* Only CDMA for now */ + /* Only CDMA and QCDM for now */ if (capabilities & CAP_CDMA) return 10; + if (capabilities & MM_PLUGIN_BASE_PORT_CAP_QCDM) + return 10; return 0; } @@ -104,7 +106,7 @@ grab_port (MMPluginBase *base, MMPluginBaseSupportsTask *task, GError **error) { - GUdevDevice *port = NULL, *physdev = NULL; + GUdevDevice *port = NULL; MMModem *modem = NULL; const char *name, *subsys, *devfile, *sysfs_path; guint32 caps; @@ -118,14 +120,6 @@ grab_port (MMPluginBase *base, return NULL; } - physdev = mm_plugin_base_supports_task_get_physdev (task); - g_assert (physdev); - sysfs_path = g_udev_device_get_sysfs_path (physdev); - if (!sysfs_path) { - g_set_error (error, 0, 0, "Could not get port's physical device sysfs path."); - return NULL; - } - subsys = g_udev_device_get_subsystem (port); name = g_udev_device_get_name (port); @@ -135,6 +129,7 @@ grab_port (MMPluginBase *base, return NULL; } + sysfs_path = mm_plugin_base_supports_task_get_physdev_path (task); if (!existing) { if (caps & CAP_CDMA) { modem = mm_modem_anydata_cdma_new (sysfs_path, @@ -150,12 +145,15 @@ grab_port (MMPluginBase *base, return NULL; } } - } else { - if (caps & CAP_CDMA) { - modem = existing; - if (!mm_modem_grab_port (modem, subsys, name, MM_PORT_TYPE_UNKNOWN, NULL, error)) - return NULL; - } + } else if (get_level_for_capabilities (caps)) { + MMPortType ptype = MM_PORT_TYPE_UNKNOWN; + + if (caps & MM_PLUGIN_BASE_PORT_CAP_QCDM) + ptype = MM_PORT_TYPE_QCDM; + + modem = existing; + if (!mm_modem_grab_port (modem, subsys, name, ptype, NULL, error)) + return NULL; } return modem; diff --git a/plugins/mm-plugin-generic.c b/plugins/mm-plugin-generic.c index e5e2ade..cdf2c66 100644 --- a/plugins/mm-plugin-generic.c +++ b/plugins/mm-plugin-generic.c @@ -59,6 +59,8 @@ get_level_for_capabilities (guint32 capabilities) return 5; if (capabilities & CAP_CDMA) return 5; + if (capabilities & MM_PLUGIN_BASE_PORT_CAP_QCDM) + return 5; return 0; } @@ -106,7 +108,7 @@ grab_port (MMPluginBase *base, MMPluginBaseSupportsTask *task, GError **error) { - GUdevDevice *port = NULL, *physdev = NULL; + GUdevDevice *port = NULL; MMModem *modem = NULL; const char *name, *subsys, *devfile, *sysfs_path, *driver; guint32 caps; @@ -131,15 +133,8 @@ grab_port (MMPluginBase *base, } } - physdev = mm_plugin_base_supports_task_get_physdev (task); - g_assert (physdev); - sysfs_path = g_udev_device_get_sysfs_path (physdev); - if (!sysfs_path) { - g_set_error (error, 0, 0, "Could not get port's physical device sysfs path."); - return NULL; - } - caps = mm_plugin_base_supports_task_get_probed_capabilities (task); + sysfs_path = mm_plugin_base_supports_task_get_physdev_path (task); if (!existing) { if (caps & MM_PLUGIN_BASE_PORT_CAP_GSM) { modem = mm_generic_gsm_new (sysfs_path, @@ -159,12 +154,15 @@ grab_port (MMPluginBase *base, return NULL; } } - } else { - if (caps & (MM_PLUGIN_BASE_PORT_CAP_GSM | CAP_CDMA)) { - modem = existing; - if (!mm_modem_grab_port (modem, subsys, name, MM_PORT_TYPE_UNKNOWN, NULL, error)) - return NULL; - } + } else if (get_level_for_capabilities (caps)) { + MMPortType ptype = MM_PORT_TYPE_UNKNOWN; + + if (caps & MM_PLUGIN_BASE_PORT_CAP_QCDM) + ptype = MM_PORT_TYPE_QCDM; + + modem = existing; + if (!mm_modem_grab_port (modem, subsys, name, ptype, NULL, error)) + return NULL; } return modem; diff --git a/plugins/mm-plugin-gobi.c b/plugins/mm-plugin-gobi.c index 77da965..fbe3878 100644 --- a/plugins/mm-plugin-gobi.c +++ b/plugins/mm-plugin-gobi.c @@ -102,7 +102,7 @@ grab_port (MMPluginBase *base, MMPluginBaseSupportsTask *task, GError **error) { - GUdevDevice *port = NULL, *physdev = NULL; + GUdevDevice *port = NULL; MMModem *modem = NULL; const char *name, *subsys, *sysfs_path; guint32 caps; @@ -110,18 +110,11 @@ grab_port (MMPluginBase *base, port = mm_plugin_base_supports_task_get_port (task); g_assert (port); - physdev = mm_plugin_base_supports_task_get_physdev (task); - g_assert (physdev); - sysfs_path = g_udev_device_get_sysfs_path (physdev); - if (!sysfs_path) { - g_set_error (error, 0, 0, "Could not get port's physical device sysfs path."); - return NULL; - } - subsys = g_udev_device_get_subsystem (port); name = g_udev_device_get_name (port); caps = mm_plugin_base_supports_task_get_probed_capabilities (task); + sysfs_path = mm_plugin_base_supports_task_get_physdev_path (task); if (!existing) { if (caps & MM_PLUGIN_BASE_PORT_CAP_GSM) { modem = mm_modem_gobi_gsm_new (sysfs_path, @@ -141,12 +134,10 @@ grab_port (MMPluginBase *base, return NULL; } } - } else { - if (caps & (MM_PLUGIN_BASE_PORT_CAP_GSM | CAP_CDMA)) { - modem = existing; - if (!mm_modem_grab_port (modem, subsys, name, MM_PORT_TYPE_UNKNOWN, NULL, error)) - return NULL; - } + } else if (get_level_for_capabilities (caps)) { + modem = existing; + if (!mm_modem_grab_port (modem, subsys, name, MM_PORT_TYPE_UNKNOWN, NULL, error)) + return NULL; } return modem; diff --git a/plugins/mm-plugin-hso.c b/plugins/mm-plugin-hso.c index 8493c9c..dc0a8fc 100644 --- a/plugins/mm-plugin-hso.c +++ b/plugins/mm-plugin-hso.c @@ -100,7 +100,7 @@ grab_port (MMPluginBase *base, MMPluginBaseSupportsTask *task, GError **error) { - GUdevDevice *port = NULL, *physdev = NULL; + GUdevDevice *port = NULL; MMModem *modem = NULL; const char *name, *subsys, *sysfs_path; char *devfile; @@ -131,18 +131,11 @@ grab_port (MMPluginBase *base, } } - physdev = mm_plugin_base_supports_task_get_physdev (task); - g_assert (physdev); - sysfs_path = g_udev_device_get_sysfs_path (physdev); - if (!sysfs_path) { - g_set_error (error, 0, 0, "Could not get port's physical device sysfs path."); - goto out; - } - caps = mm_plugin_base_supports_task_get_probed_capabilities (task); if (!(caps & MM_PLUGIN_BASE_PORT_CAP_GSM) && strcmp (subsys, "net")) goto out; + sysfs_path = mm_plugin_base_supports_task_get_physdev_path (task); if (!existing) { modem = mm_modem_hso_new (sysfs_path, mm_plugin_base_supports_task_get_driver (task), diff --git a/plugins/mm-plugin-huawei.c b/plugins/mm-plugin-huawei.c index ad799f0..2993689 100644 --- a/plugins/mm-plugin-huawei.c +++ b/plugins/mm-plugin-huawei.c @@ -26,6 +26,7 @@ #include "mm-modem-huawei-gsm.h" #include "mm-modem-huawei-cdma.h" #include "mm-serial-parsers.h" +#include "mm-at-serial-port.h" G_DEFINE_TYPE (MMPluginHuawei, mm_plugin_huawei, MM_TYPE_PLUGIN_BASE) @@ -54,6 +55,8 @@ get_level_for_capabilities (guint32 capabilities) return 10; if (capabilities & CAP_CDMA) return 10; + if (capabilities & MM_PLUGIN_BASE_PORT_CAP_QCDM) + return 10; return 0; } @@ -69,9 +72,14 @@ probe_result (MMPluginBase *base, #define TAG_SUPPORTS_INFO "huawei-supports-info" typedef struct { - MMSerialPort *serial; + MMAtSerialPort *serial; guint id; - gboolean secondary; + MMPortType ptype; + /* Whether or not there's already a detected modem that "owns" this port, + * in which case we'll claim it, but if no capabilities are detected it'll + * just be ignored. + */ + gboolean parent_modem; } HuaweiSupportsInfo; static void @@ -100,13 +108,13 @@ probe_secondary_supported (gpointer user_data) info->serial = NULL; /* Yay, supported, we got an unsolicited message */ - info->secondary = TRUE; + info->ptype = MM_PORT_TYPE_SECONDARY; mm_plugin_base_supports_task_complete (task, 10); return FALSE; } static void -probe_secondary_handle_msg (MMSerialPort *port, +probe_secondary_handle_msg (MMAtSerialPort *port, GMatchInfo *match_info, gpointer user_data) { @@ -123,24 +131,30 @@ probe_secondary_timeout (gpointer user_data) { MMPluginBaseSupportsTask *task = user_data; HuaweiSupportsInfo *info; + guint level = 0; info = g_object_get_data (G_OBJECT (task), TAG_SUPPORTS_INFO); info->id = 0; g_object_unref (info->serial); info->serial = NULL; - /* Not supported by this plugin */ - mm_plugin_base_supports_task_complete (task, 0); + /* Supported, but ignored if this port's parent device is already a modem */ + if (info->parent_modem) { + info->ptype = MM_PORT_TYPE_IGNORED; + level = 10; + } + + mm_plugin_base_supports_task_complete (task, level); return FALSE; } static void -add_regex (MMSerialPort *port, const char *match, gpointer user_data) +add_regex (MMAtSerialPort *port, const char *match, gpointer user_data) { GRegex *regex; regex = g_regex_new (match, G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); - mm_serial_port_add_unsolicited_msg_handler (port, regex, probe_secondary_handle_msg, user_data, NULL); + mm_at_serial_port_add_unsolicited_msg_handler (port, regex, probe_secondary_handle_msg, user_data, NULL); g_regex_unref (regex); } @@ -207,14 +221,15 @@ supports_port (MMPluginBase *base, /* Listen for Huawei-specific unsolicited messages */ info = g_malloc0 (sizeof (HuaweiSupportsInfo)); + info->parent_modem = !!existing; - info->serial = mm_serial_port_new (name, MM_PORT_TYPE_PRIMARY); + info->serial = mm_at_serial_port_new (name, MM_PORT_TYPE_PRIMARY); g_object_set (G_OBJECT (info->serial), MM_PORT_CARRIER_DETECT, FALSE, NULL); - mm_serial_port_set_response_parser (info->serial, - mm_serial_parser_v1_parse, - mm_serial_parser_v1_new (), - mm_serial_parser_v1_destroy); + mm_at_serial_port_set_response_parser (info->serial, + mm_serial_parser_v1_parse, + mm_serial_parser_v1_new (), + mm_serial_parser_v1_destroy); add_regex (info->serial, "\\r\\n\\^RSSI:(\\d+)\\r\\n", task); add_regex (info->serial, "\\r\\n\\^MODE:(\\d),(\\d)\\r\\n", task); @@ -222,12 +237,12 @@ supports_port (MMPluginBase *base, add_regex (info->serial, "\\r\\n\\^BOOT:.+\\r\\n", task); add_regex (info->serial, "\\r\\r\\^BOOT:.+\\r\\r", task); - info->id = g_timeout_add (5000, probe_secondary_timeout, task); + info->id = g_timeout_add_seconds (7, probe_secondary_timeout, task); g_object_set_data_full (G_OBJECT (task), TAG_SUPPORTS_INFO, info, huawei_supports_info_destroy); - if (!mm_serial_port_open (info->serial, &error)) { + if (!mm_serial_port_open (MM_SERIAL_PORT (info->serial), &error)) { g_warning ("%s: (Huawei) %s: couldn't open serial port: (%d) %s", __func__, name, error ? error->code : -1, @@ -249,7 +264,7 @@ grab_port (MMPluginBase *base, MMPluginBaseSupportsTask *task, GError **error) { - GUdevDevice *port = NULL, *physdev = NULL; + GUdevDevice *port = NULL; MMModem *modem = NULL; const char *name, *subsys, *devfile, *sysfs_path; guint32 caps; @@ -264,14 +279,6 @@ grab_port (MMPluginBase *base, return NULL; } - physdev = mm_plugin_base_supports_task_get_physdev (task); - g_assert (physdev); - sysfs_path = g_udev_device_get_sysfs_path (physdev); - if (!sysfs_path) { - g_set_error (error, 0, 0, "Could not get port's physical device sysfs path."); - return NULL; - } - subsys = g_udev_device_get_subsystem (port); name = g_udev_device_get_name (port); @@ -281,18 +288,12 @@ grab_port (MMPluginBase *base, } caps = mm_plugin_base_supports_task_get_probed_capabilities (task); + sysfs_path = mm_plugin_base_supports_task_get_physdev_path (task); if (!existing) { if (caps & MM_PLUGIN_BASE_PORT_CAP_GSM) { - if (product == 0x1001) { - /* This modem is handled by generic GSM driver */ - modem = mm_generic_gsm_new (sysfs_path, - mm_plugin_base_supports_task_get_driver (task), - mm_plugin_get_name (MM_PLUGIN (base))); - } else { - modem = mm_modem_huawei_gsm_new (sysfs_path, - mm_plugin_base_supports_task_get_driver (task), - mm_plugin_get_name (MM_PLUGIN (base))); - } + modem = mm_modem_huawei_gsm_new (sysfs_path, + mm_plugin_base_supports_task_get_driver (task), + mm_plugin_get_name (MM_PLUGIN (base))); } else if (caps & CAP_CDMA) { modem = mm_modem_huawei_cdma_new (sysfs_path, mm_plugin_base_supports_task_get_driver (task), @@ -312,8 +313,10 @@ grab_port (MMPluginBase *base, MMPortType ptype = MM_PORT_TYPE_UNKNOWN; info = g_object_get_data (G_OBJECT (task), TAG_SUPPORTS_INFO); - if (info && info->secondary && (product != 0x1001)) - ptype = MM_PORT_TYPE_SECONDARY; + if (info) + ptype = info->ptype; + else if (caps & MM_PLUGIN_BASE_PORT_CAP_QCDM) + ptype = MM_PORT_TYPE_QCDM; modem = existing; if (!mm_modem_grab_port (modem, subsys, name, ptype, NULL, error)) diff --git a/plugins/mm-plugin-longcheer.c b/plugins/mm-plugin-longcheer.c index 5c19b13..dbbe186 100644 --- a/plugins/mm-plugin-longcheer.c +++ b/plugins/mm-plugin-longcheer.c @@ -11,12 +11,13 @@ * GNU General Public License for more details: * * Copyright (C) 2008 - 2009 Novell, Inc. - * Copyright (C) 2009 Red Hat, Inc. + * Copyright (C) 2009 - 2010 Red Hat, Inc. */ #include <string.h> #include <gmodule.h> #include "mm-plugin-longcheer.h" +#include "mm-modem-longcheer-gsm.h" #include "mm-generic-gsm.h" #include "mm-generic-cdma.h" @@ -47,6 +48,8 @@ get_level_for_capabilities (guint32 capabilities) return 10; if (capabilities & CAP_CDMA) return 10; + if (capabilities & MM_PLUGIN_BASE_PORT_CAP_QCDM) + return 10; return 0; } @@ -80,7 +83,8 @@ supports_port (MMPluginBase *base, if (!mm_plugin_base_get_device_ids (base, subsys, name, &vendor, NULL)) return MM_PLUGIN_SUPPORTS_PORT_UNSUPPORTED; - if (vendor != 0x1c9e) + /* Longcheer and TAMobile */ + if (vendor != 0x1c9e && vendor != 0x1bbb) return MM_PLUGIN_SUPPORTS_PORT_UNSUPPORTED; if (mm_plugin_base_get_cached_port_capabilities (base, port, &cached)) { @@ -105,7 +109,7 @@ grab_port (MMPluginBase *base, MMPluginBaseSupportsTask *task, GError **error) { - GUdevDevice *port = NULL, *physdev = NULL; + GUdevDevice *port = NULL; MMModem *modem = NULL; const char *name, *subsys, *sysfs_path; guint32 caps; @@ -131,23 +135,16 @@ grab_port (MMPluginBase *base, && g_udev_device_get_property_as_boolean (port, "ID_MM_LONGCHEER_TAGGED")) ptype = MM_PORT_TYPE_IGNORED; - physdev = mm_plugin_base_supports_task_get_physdev (task); - g_assert (physdev); - sysfs_path = g_udev_device_get_sysfs_path (physdev); - if (!sysfs_path) { - g_set_error (error, 0, 0, "Could not get port's physical device sysfs path."); - return NULL; - } - subsys = g_udev_device_get_subsystem (port); name = g_udev_device_get_name (port); caps = mm_plugin_base_supports_task_get_probed_capabilities (task); + sysfs_path = mm_plugin_base_supports_task_get_physdev_path (task); if (!existing) { if (caps & MM_PLUGIN_BASE_PORT_CAP_GSM) { - modem = mm_generic_gsm_new (sysfs_path, - mm_plugin_base_supports_task_get_driver (task), - mm_plugin_get_name (MM_PLUGIN (base))); + modem = mm_modem_longcheer_gsm_new (sysfs_path, + mm_plugin_base_supports_task_get_driver (task), + mm_plugin_get_name (MM_PLUGIN (base))); } else if (caps & CAP_CDMA) { modem = mm_generic_cdma_new (sysfs_path, mm_plugin_base_supports_task_get_driver (task), @@ -162,12 +159,13 @@ grab_port (MMPluginBase *base, return NULL; } } - } else { - if (caps & (MM_PLUGIN_BASE_PORT_CAP_GSM | CAP_CDMA)) { - modem = existing; - if (!mm_modem_grab_port (modem, subsys, name, ptype, NULL, error)) - return NULL; - } + } else if (get_level_for_capabilities (caps)) { + if (caps & MM_PLUGIN_BASE_PORT_CAP_QCDM) + ptype = MM_PORT_TYPE_QCDM; + + modem = existing; + if (!mm_modem_grab_port (modem, subsys, name, ptype, NULL, error)) + return NULL; } return modem; diff --git a/plugins/mm-plugin-mbm.c b/plugins/mm-plugin-mbm.c index a380f98..5554d84 100644 --- a/plugins/mm-plugin-mbm.c +++ b/plugins/mm-plugin-mbm.c @@ -14,10 +14,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - * */ #include <string.h> @@ -66,9 +62,12 @@ supports_port (MMPluginBase *base, MMModem *existing, MMPluginBaseSupportsTask *task) { + GUdevClient *client; + const char *sys[] = { "tty", "net", NULL }; GUdevDevice *port, *physdev; guint32 cached = 0, level; - const char *driver, *subsys; + const char *driver, *subsys, *physdev_path; + gboolean is_mbm; /* Can't do anything with non-serial ports */ port = mm_plugin_base_supports_task_get_port (task); @@ -83,9 +82,23 @@ supports_port (MMPluginBase *base, if (!driver) return MM_PLUGIN_SUPPORTS_PORT_UNSUPPORTED; - physdev = mm_plugin_base_supports_task_get_physdev (task); + client = g_udev_client_new (sys); + if (!client) { + g_warning ("mbm: could not get udev client."); + return MM_PLUGIN_SUPPORTS_PORT_UNSUPPORTED; + } + + /* Look up the port's physical device and see if this port is really an + * 'mbm' modem, since we have no other way of telling. + */ + physdev_path = mm_plugin_base_supports_task_get_physdev_path (task); + physdev = g_udev_client_query_by_sysfs_path (client, physdev_path); g_assert (physdev); - if (!g_udev_device_get_property_as_boolean (physdev, "ID_MM_ERICSSON_MBM")) + + is_mbm = g_udev_device_get_property_as_boolean (physdev, "ID_MM_ERICSSON_MBM"); + g_object_unref (client); + + if (!is_mbm) return MM_PLUGIN_SUPPORTS_PORT_UNSUPPORTED; if (!strcmp (subsys, "net")) { @@ -115,7 +128,7 @@ grab_port (MMPluginBase *base, MMPluginBaseSupportsTask *task, GError **error) { - GUdevDevice *port = NULL, *physdev = NULL; + GUdevDevice *port = NULL; MMModem *modem = NULL; const char *name, *subsys, *sysfs_path; guint32 caps; @@ -123,14 +136,6 @@ grab_port (MMPluginBase *base, port = mm_plugin_base_supports_task_get_port (task); g_assert (port); - physdev = mm_plugin_base_supports_task_get_physdev (task); - g_assert (physdev); - sysfs_path = g_udev_device_get_sysfs_path (physdev); - if (!sysfs_path) { - g_set_error (error, 0, 0, "Could not get port's physical device sysfs path."); - return NULL; - } - subsys = g_udev_device_get_subsystem (port); name = g_udev_device_get_name (port); @@ -138,6 +143,7 @@ grab_port (MMPluginBase *base, if (!(caps & MM_PLUGIN_BASE_PORT_CAP_GSM) && strcmp (subsys, "net")) return NULL; + sysfs_path = mm_plugin_base_supports_task_get_physdev_path (task); if (!existing) { modem = mm_modem_mbm_new (sysfs_path, mm_plugin_base_supports_task_get_driver (task), diff --git a/plugins/mm-plugin-mbm.h b/plugins/mm-plugin-mbm.h index c0e73b5..c478f11 100644 --- a/plugins/mm-plugin-mbm.h +++ b/plugins/mm-plugin-mbm.h @@ -14,10 +14,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - * */ #ifndef MM_PLUGIN_MBM_H diff --git a/plugins/mm-plugin-moto-c.c b/plugins/mm-plugin-moto-c.c index 5b32a1e..d798af4 100644 --- a/plugins/mm-plugin-moto-c.c +++ b/plugins/mm-plugin-moto-c.c @@ -37,18 +37,21 @@ mm_plugin_create (void) /*****************************************************************************/ +static guint32 +get_level_for_capabilities (guint32 capabilities) +{ + if (capabilities & MM_PLUGIN_BASE_PORT_CAP_GSM) + return 10; + return 0; +} + static void probe_result (MMPluginBase *base, MMPluginBaseSupportsTask *task, guint32 capabilities, gpointer user_data) { - guint32 level = 0; - - if (capabilities & MM_PLUGIN_BASE_PORT_CAP_GSM) - level = 10; - - mm_plugin_base_supports_task_complete (task, level); + mm_plugin_base_supports_task_complete (task, get_level_for_capabilities (capabilities)); } static MMPluginSupportsResult @@ -58,7 +61,7 @@ supports_port (MMPluginBase *base, { GUdevDevice *port; const char *tmp; - guint32 cached = 0; + guint32 cached = 0, level; /* Can't do anything with non-serial ports */ port = mm_plugin_base_supports_task_get_port (task); @@ -78,7 +81,8 @@ supports_port (MMPluginBase *base, return MM_PLUGIN_SUPPORTS_PORT_UNSUPPORTED; if (mm_plugin_base_get_cached_port_capabilities (base, port, &cached)) { - if (cached & MM_PLUGIN_BASE_PORT_CAP_GSM) { + level = get_level_for_capabilities (cached); + if (level) { mm_plugin_base_supports_task_complete (task, 10); return MM_PLUGIN_SUPPORTS_PORT_IN_PROGRESS; } @@ -98,7 +102,7 @@ grab_port (MMPluginBase *base, MMPluginBaseSupportsTask *task, GError **error) { - GUdevDevice *port = NULL, *physdev = NULL; + GUdevDevice *port = NULL; MMModem *modem = NULL; const char *name, *subsys, *devfile, *sysfs_path; @@ -111,17 +115,10 @@ grab_port (MMPluginBase *base, return NULL; } - physdev = mm_plugin_base_supports_task_get_physdev (task); - g_assert (physdev); - sysfs_path = g_udev_device_get_sysfs_path (physdev); - if (!sysfs_path) { - g_set_error (error, 0, 0, "Could not get port's physical device sysfs path."); - return NULL; - } - subsys = g_udev_device_get_subsystem (port); name = g_udev_device_get_name (port); + sysfs_path = mm_plugin_base_supports_task_get_physdev_path (task); if (!existing) { modem = mm_modem_moto_c_gsm_new (sysfs_path, mm_plugin_base_supports_task_get_driver (task), diff --git a/plugins/mm-plugin-nokia.c b/plugins/mm-plugin-nokia.c index e088323..2d0d6af 100644 --- a/plugins/mm-plugin-nokia.c +++ b/plugins/mm-plugin-nokia.c @@ -105,7 +105,7 @@ grab_port (MMPluginBase *base, MMPluginBaseSupportsTask *task, GError **error) { - GUdevDevice *port = NULL, *physdev = NULL; + GUdevDevice *port = NULL; MMModem *modem = NULL; const char *name, *subsys, *devfile, *sysfs_path; guint32 caps; @@ -119,18 +119,11 @@ grab_port (MMPluginBase *base, return NULL; } - physdev = mm_plugin_base_supports_task_get_physdev (task); - g_assert (physdev); - sysfs_path = g_udev_device_get_sysfs_path (physdev); - if (!sysfs_path) { - g_set_error (error, 0, 0, "Could not get port's physical device sysfs path."); - return NULL; - } - subsys = g_udev_device_get_subsystem (port); name = g_udev_device_get_name (port); caps = mm_plugin_base_supports_task_get_probed_capabilities (task); + sysfs_path = mm_plugin_base_supports_task_get_physdev_path (task); if (!existing) { if (caps & MM_PLUGIN_BASE_PORT_CAP_GSM) { modem = mm_modem_nokia_new (sysfs_path, @@ -150,12 +143,10 @@ grab_port (MMPluginBase *base, return NULL; } } - } else { - if (caps & (MM_PLUGIN_BASE_PORT_CAP_GSM | CAP_CDMA)) { - modem = existing; - if (!mm_modem_grab_port (modem, subsys, name, MM_PORT_TYPE_UNKNOWN, NULL, error)) - return NULL; - } + } else if (get_level_for_capabilities (caps)) { + modem = existing; + if (!mm_modem_grab_port (modem, subsys, name, MM_PORT_TYPE_UNKNOWN, NULL, error)) + return NULL; } return modem; diff --git a/plugins/mm-plugin-novatel.c b/plugins/mm-plugin-novatel.c index 48ff7ec..a968836 100644 --- a/plugins/mm-plugin-novatel.c +++ b/plugins/mm-plugin-novatel.c @@ -18,7 +18,7 @@ #include <gmodule.h> #include "mm-plugin-novatel.h" #include "mm-modem-novatel-gsm.h" -#include "mm-generic-cdma.h" +#include "mm-modem-novatel-cdma.h" G_DEFINE_TYPE (MMPluginNovatel, mm_plugin_novatel, MM_TYPE_PLUGIN_BASE) @@ -47,6 +47,8 @@ get_level_for_capabilities (guint32 capabilities) return 10; if (capabilities & CAP_CDMA) return 10; + if (capabilities & MM_PLUGIN_BASE_PORT_CAP_QCDM) + return 10; return 0; } @@ -109,7 +111,7 @@ grab_port (MMPluginBase *base, MMPluginBaseSupportsTask *task, GError **error) { - GUdevDevice *port = NULL, *physdev = NULL; + GUdevDevice *port = NULL; MMModem *modem = NULL; const char *name, *subsys, *devfile, *sysfs_path; guint32 caps; @@ -123,29 +125,22 @@ grab_port (MMPluginBase *base, return NULL; } - physdev = mm_plugin_base_supports_task_get_physdev (task); - g_assert (physdev); - sysfs_path = g_udev_device_get_sysfs_path (physdev); - if (!sysfs_path) { - g_set_error (error, 0, 0, "Could not get port's physical device sysfs path."); - return NULL; - } - subsys = g_udev_device_get_subsystem (port); name = g_udev_device_get_name (port); caps = mm_plugin_base_supports_task_get_probed_capabilities (task); + sysfs_path = mm_plugin_base_supports_task_get_physdev_path (task); if (!existing) { if (caps & MM_PLUGIN_BASE_PORT_CAP_GSM) { modem = mm_modem_novatel_gsm_new (sysfs_path, mm_plugin_base_supports_task_get_driver (task), mm_plugin_get_name (MM_PLUGIN (base))); } else if (caps & CAP_CDMA) { - modem = mm_generic_cdma_new (sysfs_path, - mm_plugin_base_supports_task_get_driver (task), - mm_plugin_get_name (MM_PLUGIN (base)), - !!(caps & MM_PLUGIN_BASE_PORT_CAP_IS856), - !!(caps & MM_PLUGIN_BASE_PORT_CAP_IS856_A)); + modem = mm_modem_novatel_cdma_new (sysfs_path, + mm_plugin_base_supports_task_get_driver (task), + mm_plugin_get_name (MM_PLUGIN (base)), + !!(caps & MM_PLUGIN_BASE_PORT_CAP_IS856), + !!(caps & MM_PLUGIN_BASE_PORT_CAP_IS856_A)); } if (modem) { @@ -154,12 +149,15 @@ grab_port (MMPluginBase *base, return NULL; } } - } else { - if (caps & (MM_PLUGIN_BASE_PORT_CAP_GSM | CAP_CDMA)) { - modem = existing; - if (!mm_modem_grab_port (modem, subsys, name, MM_PORT_TYPE_UNKNOWN, NULL, error)) - return NULL; - } + } else if (get_level_for_capabilities (caps)) { + MMPortType ptype = MM_PORT_TYPE_UNKNOWN; + + if (caps & MM_PLUGIN_BASE_PORT_CAP_QCDM) + ptype = MM_PORT_TYPE_QCDM; + + modem = existing; + if (!mm_modem_grab_port (modem, subsys, name, ptype, NULL, error)) + return NULL; } return modem; diff --git a/plugins/mm-plugin-option.c b/plugins/mm-plugin-option.c index d4c402d..101f9bd 100644 --- a/plugins/mm-plugin-option.c +++ b/plugins/mm-plugin-option.c @@ -101,7 +101,7 @@ grab_port (MMPluginBase *base, MMPluginBaseSupportsTask *task, GError **error) { - GUdevDevice *port = NULL, *physdev = NULL; + GUdevDevice *port = NULL; MMModem *modem = NULL; const char *name, *subsys, *devfile, *sysfs_path; guint32 caps; @@ -117,14 +117,6 @@ grab_port (MMPluginBase *base, return NULL; } - physdev = mm_plugin_base_supports_task_get_physdev (task); - g_assert (physdev); - sysfs_path = g_udev_device_get_sysfs_path (physdev); - if (!sysfs_path) { - g_set_error (error, 0, 0, "Could not get port's physical device sysfs path."); - return NULL; - } - subsys = g_udev_device_get_subsystem (port); name = g_udev_device_get_name (port); @@ -138,6 +130,7 @@ grab_port (MMPluginBase *base, ptype = MM_PORT_TYPE_PRIMARY; caps = mm_plugin_base_supports_task_get_probed_capabilities (task); + sysfs_path = mm_plugin_base_supports_task_get_physdev_path (task); if (!existing) { if (caps & MM_PLUGIN_BASE_PORT_CAP_GSM) { modem = mm_modem_option_new (sysfs_path, @@ -151,12 +144,13 @@ grab_port (MMPluginBase *base, return NULL; } } - } else { - if (caps & MM_PLUGIN_BASE_PORT_CAP_GSM) { - modem = existing; - if (!mm_modem_grab_port (modem, subsys, name, ptype, NULL, error)) - return NULL; - } + } else if (get_level_for_capabilities (caps)) { + if (caps & MM_PLUGIN_BASE_PORT_CAP_QCDM) + ptype = MM_PORT_TYPE_QCDM; + + modem = existing; + if (!mm_modem_grab_port (modem, subsys, name, ptype, NULL, error)) + return NULL; } return modem; diff --git a/plugins/mm-plugin-sierra.c b/plugins/mm-plugin-sierra.c index 637f46d..8ace653 100644 --- a/plugins/mm-plugin-sierra.c +++ b/plugins/mm-plugin-sierra.c @@ -123,7 +123,7 @@ grab_port (MMPluginBase *base, MMPluginBaseSupportsTask *task, GError **error) { - GUdevDevice *port = NULL, *physdev = NULL; + GUdevDevice *port = NULL; MMModem *modem = NULL; const char *name, *subsys, *devfile, *sysfs_path; guint32 caps; @@ -138,14 +138,6 @@ grab_port (MMPluginBase *base, return NULL; } - physdev = mm_plugin_base_supports_task_get_physdev (task); - g_assert (physdev); - sysfs_path = g_udev_device_get_sysfs_path (physdev); - if (!sysfs_path) { - g_set_error (error, 0, 0, "Could not get port's physical device sysfs path."); - return NULL; - } - subsys = g_udev_device_get_subsystem (port); name = g_udev_device_get_name (port); @@ -154,6 +146,7 @@ grab_port (MMPluginBase *base, ptype = MM_PORT_TYPE_SECONDARY; caps = mm_plugin_base_supports_task_get_probed_capabilities (task); + sysfs_path = mm_plugin_base_supports_task_get_physdev_path (task); if (!existing) { if ((caps & MM_PLUGIN_BASE_PORT_CAP_GSM) || (ptype != MM_PORT_TYPE_UNKNOWN)) { modem = mm_modem_sierra_gsm_new (sysfs_path, @@ -173,13 +166,10 @@ grab_port (MMPluginBase *base, return NULL; } } - } else { - if ( (caps & (MM_PLUGIN_BASE_PORT_CAP_GSM | CAP_CDMA)) - || (ptype != MM_PORT_TYPE_UNKNOWN)) { - modem = existing; - if (!mm_modem_grab_port (modem, subsys, name, ptype, NULL, error)) - return NULL; - } + } else if (get_level_for_capabilities (caps) || (ptype != MM_PORT_TYPE_UNKNOWN)) { + modem = existing; + if (!mm_modem_grab_port (modem, subsys, name, ptype, NULL, error)) + return NULL; } return modem; diff --git a/plugins/mm-plugin-simtech.c b/plugins/mm-plugin-simtech.c new file mode 100644 index 0000000..3c44873 --- /dev/null +++ b/plugins/mm-plugin-simtech.c @@ -0,0 +1,189 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details: + * + * Copyright (C) 2008 - 2009 Novell, Inc. + * Copyright (C) 2009 - 2010 Red Hat, Inc. + */ + +#include <string.h> +#include <gmodule.h> +#include "mm-plugin-simtech.h" +#include "mm-modem-simtech-gsm.h" +#include "mm-generic-gsm.h" +#include "mm-generic-cdma.h" + +G_DEFINE_TYPE (MMPluginSimtech, mm_plugin_simtech, MM_TYPE_PLUGIN_BASE) + +int mm_plugin_major_version = MM_PLUGIN_MAJOR_VERSION; +int mm_plugin_minor_version = MM_PLUGIN_MINOR_VERSION; + +G_MODULE_EXPORT MMPlugin * +mm_plugin_create (void) +{ + return MM_PLUGIN (g_object_new (MM_TYPE_PLUGIN_SIMTECH, + MM_PLUGIN_BASE_NAME, "SimTech", + NULL)); +} + +/*****************************************************************************/ + +#define CAP_CDMA (MM_PLUGIN_BASE_PORT_CAP_IS707_A | \ + MM_PLUGIN_BASE_PORT_CAP_IS707_P | \ + MM_PLUGIN_BASE_PORT_CAP_IS856 | \ + MM_PLUGIN_BASE_PORT_CAP_IS856_A) + +static guint32 +get_level_for_capabilities (guint32 capabilities) +{ + if (capabilities & MM_PLUGIN_BASE_PORT_CAP_GSM) + return 10; + if (capabilities & CAP_CDMA) + return 10; + if (capabilities & MM_PLUGIN_BASE_PORT_CAP_QCDM) + return 10; + return 0; +} + +static void +probe_result (MMPluginBase *base, + MMPluginBaseSupportsTask *task, + guint32 capabilities, + gpointer user_data) +{ + mm_plugin_base_supports_task_complete (task, get_level_for_capabilities (capabilities)); +} + +static MMPluginSupportsResult +supports_port (MMPluginBase *base, + MMModem *existing, + MMPluginBaseSupportsTask *task) +{ + GUdevDevice *port; + guint32 cached = 0, level; + guint16 vendor = 0; + const char *subsys, *name; + + /* Can't do anything with non-serial ports */ + port = mm_plugin_base_supports_task_get_port (task); + if (strcmp (g_udev_device_get_subsystem (port), "tty")) + return MM_PLUGIN_SUPPORTS_PORT_UNSUPPORTED; + + subsys = g_udev_device_get_subsystem (port); + name = g_udev_device_get_name (port); + + if (!mm_plugin_base_get_device_ids (base, subsys, name, &vendor, NULL)) + return MM_PLUGIN_SUPPORTS_PORT_UNSUPPORTED; + + /* A-Link (for now) */ + if (vendor != 0x1e0e) + return MM_PLUGIN_SUPPORTS_PORT_UNSUPPORTED; + + if (mm_plugin_base_get_cached_port_capabilities (base, port, &cached)) { + level = get_level_for_capabilities (cached); + if (level) { + mm_plugin_base_supports_task_complete (task, level); + return MM_PLUGIN_SUPPORTS_PORT_IN_PROGRESS; + } + return MM_PLUGIN_SUPPORTS_PORT_UNSUPPORTED; + } + + /* Otherwise kick off a probe */ + if (mm_plugin_base_probe_port (base, task, NULL)) + return MM_PLUGIN_SUPPORTS_PORT_IN_PROGRESS; + + return MM_PLUGIN_SUPPORTS_PORT_UNSUPPORTED; +} + +static MMModem * +grab_port (MMPluginBase *base, + MMModem *existing, + MMPluginBaseSupportsTask *task, + GError **error) +{ + GUdevDevice *port = NULL; + MMModem *modem = NULL; + const char *name, *subsys, *sysfs_path; + guint32 caps; + MMPortType ptype = MM_PORT_TYPE_UNKNOWN; + + port = mm_plugin_base_supports_task_get_port (task); + g_assert (port); + + /* Look for port type hints; just probing can't distinguish which port should + * be the data/primary port on these devices. We have to tag them based on + * what the Windows .INF files say the port layout should be. + */ + if (g_udev_device_get_property_as_boolean (port, "ID_MM_SIMTECH_PORT_TYPE_MODEM")) + ptype = MM_PORT_TYPE_PRIMARY; + else if (g_udev_device_get_property_as_boolean (port, "ID_MM_SIMTECH_PORT_TYPE_AUX")) + ptype = MM_PORT_TYPE_SECONDARY; + + /* If the device was tagged by the udev rules, then ignore any other ports + * to guard against race conditions if a device just happens to show up + * with more than two AT-capable ports. + */ + if ( (ptype == MM_PORT_TYPE_UNKNOWN) + && g_udev_device_get_property_as_boolean (port, "ID_MM_SIMTECH_TAGGED")) + ptype = MM_PORT_TYPE_IGNORED; + + subsys = g_udev_device_get_subsystem (port); + name = g_udev_device_get_name (port); + + caps = mm_plugin_base_supports_task_get_probed_capabilities (task); + sysfs_path = mm_plugin_base_supports_task_get_physdev_path (task); + if (!existing) { + if (caps & MM_PLUGIN_BASE_PORT_CAP_GSM) { + modem = mm_modem_simtech_gsm_new (sysfs_path, + mm_plugin_base_supports_task_get_driver (task), + mm_plugin_get_name (MM_PLUGIN (base))); + } else if (caps & CAP_CDMA) { + modem = mm_generic_cdma_new (sysfs_path, + mm_plugin_base_supports_task_get_driver (task), + mm_plugin_get_name (MM_PLUGIN (base)), + !!(caps & MM_PLUGIN_BASE_PORT_CAP_IS856), + !!(caps & MM_PLUGIN_BASE_PORT_CAP_IS856_A)); + } + + if (modem) { + if (!mm_modem_grab_port (modem, subsys, name, ptype, NULL, error)) { + g_object_unref (modem); + return NULL; + } + } + } else if (get_level_for_capabilities (caps)) { + if (caps & MM_PLUGIN_BASE_PORT_CAP_QCDM) + ptype = MM_PORT_TYPE_QCDM; + + modem = existing; + if (!mm_modem_grab_port (modem, subsys, name, ptype, NULL, error)) + return NULL; + } + + return modem; +} + +/*****************************************************************************/ + +static void +mm_plugin_simtech_init (MMPluginSimtech *self) +{ + g_signal_connect (self, "probe-result", G_CALLBACK (probe_result), NULL); +} + +static void +mm_plugin_simtech_class_init (MMPluginSimtechClass *klass) +{ + MMPluginBaseClass *pb_class = MM_PLUGIN_BASE_CLASS (klass); + + pb_class->supports_port = supports_port; + pb_class->grab_port = grab_port; +} diff --git a/plugins/mm-plugin-simtech.h b/plugins/mm-plugin-simtech.h new file mode 100644 index 0000000..e316056 --- /dev/null +++ b/plugins/mm-plugin-simtech.h @@ -0,0 +1,41 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details: + * + * Copyright (C) 2008 - 2009 Novell, Inc. + * Copyright (C) 2009 - 2010 Red Hat, Inc. + */ + +#ifndef MM_PLUGIN_SIMTECH_H +#define MM_PLUGIN_SIMTECH_H + +#include "mm-plugin-base.h" + +#define MM_TYPE_PLUGIN_SIMTECH (mm_plugin_simtech_get_type ()) +#define MM_PLUGIN_SIMTECH(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_PLUGIN_SIMTECH, MMPluginSimtech)) +#define MM_PLUGIN_SIMTECH_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_PLUGIN_SIMTECH, MMPluginSimtechClass)) +#define MM_IS_PLUGIN_SIMTECH(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_PLUGIN_SIMTECH)) +#define MM_IS_PLUGIN_SIMTECH_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_PLUGIN_SIMTECH)) +#define MM_PLUGIN_SIMTECH_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_PLUGIN_SIMTECH, MMPluginSimtechClass)) + +typedef struct { + MMPluginBase parent; +} MMPluginSimtech; + +typedef struct { + MMPluginBaseClass parent; +} MMPluginSimtechClass; + +GType mm_plugin_simtech_get_type (void); + +G_MODULE_EXPORT MMPlugin *mm_plugin_create (void); + +#endif /* MM_PLUGIN_SIMTECH_H */ diff --git a/plugins/mm-plugin-zte.c b/plugins/mm-plugin-zte.c index 101fb46..e943bbf 100644 --- a/plugins/mm-plugin-zte.c +++ b/plugins/mm-plugin-zte.c @@ -47,6 +47,8 @@ get_level_for_capabilities (guint32 capabilities) return 10; if (capabilities & CAP_CDMA) return 10; + if (capabilities & MM_PLUGIN_BASE_PORT_CAP_QCDM) + return 10; return 0; } @@ -100,7 +102,7 @@ supports_port (MMPluginBase *base, * 1235f71b20c92cded4abd976ccc5010649aae1a0 and * f38ad328acfdc6ce29dd1380602c546b064161ae for more details. */ - mm_plugin_base_supports_task_set_custom_init_command (task, "ATE0+CPMS?", 3, 4, TRUE); + mm_plugin_base_supports_task_set_custom_init_command (task, "ATE0+CPMS?", 3, 4, FALSE); if (mm_plugin_base_probe_port (base, task, NULL)) return MM_PLUGIN_SUPPORTS_PORT_IN_PROGRESS; @@ -114,7 +116,7 @@ grab_port (MMPluginBase *base, MMPluginBaseSupportsTask *task, GError **error) { - GUdevDevice *port = NULL, *physdev = NULL; + GUdevDevice *port = NULL; MMModem *modem = NULL; const char *name, *subsys, *sysfs_path; guint32 caps; @@ -129,18 +131,11 @@ grab_port (MMPluginBase *base, else if (g_udev_device_get_property_as_boolean (port, "ID_MM_ZTE_PORT_TYPE_AUX")) ptype = MM_PORT_TYPE_SECONDARY; - physdev = mm_plugin_base_supports_task_get_physdev (task); - g_assert (physdev); - sysfs_path = g_udev_device_get_sysfs_path (physdev); - if (!sysfs_path) { - g_set_error (error, 0, 0, "Could not get port's physical device sysfs path."); - return NULL; - } - subsys = g_udev_device_get_subsystem (port); name = g_udev_device_get_name (port); caps = mm_plugin_base_supports_task_get_probed_capabilities (task); + sysfs_path = mm_plugin_base_supports_task_get_physdev_path (task); if (!existing) { if (caps & MM_PLUGIN_BASE_PORT_CAP_GSM) { modem = mm_modem_zte_new (sysfs_path, @@ -160,12 +155,13 @@ grab_port (MMPluginBase *base, return NULL; } } - } else { - if (caps & (MM_PLUGIN_BASE_PORT_CAP_GSM | CAP_CDMA)) { - modem = existing; - if (!mm_modem_grab_port (modem, subsys, name, ptype, NULL, error)) - return NULL; - } + } else if (get_level_for_capabilities (caps)) { + if (caps & MM_PLUGIN_BASE_PORT_CAP_QCDM) + ptype = MM_PORT_TYPE_QCDM; + + modem = existing; + if (!mm_modem_grab_port (modem, subsys, name, ptype, NULL, error)) + return NULL; } return modem; |