diff options
author | Guido Günther <agx@sigxcpu.org> | 2014-02-05 08:38:15 +0100 |
---|---|---|
committer | Guido Günther <agx@sigxcpu.org> | 2014-02-05 08:38:15 +0100 |
commit | 87bd9deec22af69bb27226254803ac5c63b18d78 (patch) | |
tree | c34d42bf75c20b3fd740e4cd59e45aa6901a9fed |
Imported Upstream version 0.3upstream/0.3
140 files changed, 34873 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..c6c7fda --- /dev/null +++ b/.gitignore @@ -0,0 +1,29 @@ +INSTALL +Makefile +Makefile.in +aclocal.m4 +autom4te.cache* +compile +config.* +configure +depcomp +install-sh +ltmain.sh +missing +stamp-h1 +*~ +*.o +*.lo +*.la +*.loT +libtool +.deps +.libs +*-glue.h +org.freedesktop.ModemManager.service +ModemManager.pc +marshallers/mm-marshal.[ch] +src/modem-manager +docs/spec.html +callouts/mm-modem-probe + @@ -0,0 +1,2 @@ +Tambet Ingo <tambet@gmail.com> + @@ -0,0 +1,340 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + <one line to give the program's name and a brief idea of what it does.> + Copyright (C) <year> <name of author> + + 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. + + 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + <signature of Ty Coon>, 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/ChangeLog b/ChangeLog new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/ChangeLog diff --git a/Makefile.am b/Makefile.am new file mode 100644 index 0000000..eee0847 --- /dev/null +++ b/Makefile.am @@ -0,0 +1,49 @@ +if WITH_DOCS + +all: + +XSLTPROC = xsltproc --xinclude --nonet + +XMLS = $(wildcard introspection/mm-*.xml) +# Figure out if we need ASYNC_INTROSPECT and add it later + +GENERATED_FILES = \ + docs/spec.html + +docs/spec.html: $(XMLS) introspection/all.xml doc-generator.xsl + @install -d docs + $(XSLTPROC) doc-generator.xsl introspection/all.xml > $@ + +all: $(GENERATED_FILES) + +CLEANFILES = $(GENERATED_FILES) +endif + +SUBDIRS = marshallers src plugins introspection test + +dbusservicedir = $(DBUS_SYS_DIR) +dbusservice_DATA = org.freedesktop.ModemManager.conf + +dbusactivationdir = $(datadir)/dbus-1/system-services +dbusactivation_in_files = org.freedesktop.ModemManager.service.in +dbusactivation_DATA = $(dbusactivation_in_files:.service.in=.service) + +%service: %service.in + $(edit) $< >$@ + +edit = @sed \ + -e 's|@sbindir[@]|$(sbindir)|g' \ + -e 's|@sysconfdir[@]|$(sysconfdir)|g' \ + -e 's|@localstatedir[@]|$(localstatedir)|g' \ + -e 's|@libexecdir[@]|$(libexecdir)|g' + +DISTCHECK_CONFIGURE_FLAGS = --with-udev-base-dir=$dc_install_base + +DISTCLEANFILES = \ + $(dbusactivation_DATA) + + +EXTRA_DIST = \ + doc-generator.xsl \ + $(dbusservice_DATA) \ + $(dbusactivation_in_files) @@ -0,0 +1,34 @@ +ModemManager. +The problem ModemManager tries to solve is to provide a unified high level API +for communicating with (mobile broadband) modems. While the basic commands are +standardized, the more advanced operations (like signal quality monitoring +while connected) varies a lot. + +Using. +ModemManager is a system daemon and is not meant to be used directly from +the command line. However, since it provides DBus API, it is possible to use +'dbus-send' command to control it from the terminal. There's an example +program (tests/mm-test.py) that demonstrates the basic API usage. + +Implementation. +ModemManager is a DBus system bus activated service (meaning it's started +automatically when a request arrives). It is written in C. The devices are +queried from HAL and automatically updated based on hardware events. There's +a GInterface (MMModem) that defines the modem interface and any device specific +implementation must implement it. There are two generic MMModem implementations +to support the basic operations (one for GSM, one for CDMA,) which are common +for all cards. + +Plugins. +Plugins are loaded on startup, and must implement the MMPlugin interface. It +consists of a couple of methods which tell the daemon whether the plugin +supports a HAL UDI and to create custom MMModem implementations. It most likely +makes sense to derive custom modem implementations from one of the generic +classes and just add (or override) operations which are not standard. There's a +fully working plugin in the plugins/ directory for Huawei cards that can be +used as an example for writing new plugins. Writing new plugins is highly +encouraged! + +API. +The API is open for changes, so if you're writing a plugin and need to add or +change some public method, feel free to suggest it! diff --git a/autogen.sh b/autogen.sh new file mode 100755 index 0000000..d2ed9b5 --- /dev/null +++ b/autogen.sh @@ -0,0 +1,20 @@ +#!/bin/sh +# Run this to generate all the initial makefiles, etc. + +srcdir=`dirname $0` +test -z "$srcdir" && srcdir=. +REQUIRED_AUTOMAKE_VERSION=1.7 +PKG_NAME=ModemManager + +(test -f $srcdir/configure.ac \ + && test -f $srcdir/src/mm-modem.c) || { + echo -n "**Error**: Directory "\`$srcdir\'" does not look like the" + echo " top-level $PKG_NAME directory" + exit 1 +} + +(cd $srcdir; + autoreconf --install --symlink && + autoreconf && + ./configure --enable-maintainer-mode $@ +) diff --git a/configure.ac b/configure.ac new file mode 100644 index 0000000..aae718e --- /dev/null +++ b/configure.ac @@ -0,0 +1,133 @@ +AC_PREREQ(2.52) + +AC_INIT(ModemManager, 0.3, dcbw@redhat.com, ModemManager) +AM_INIT_AUTOMAKE([1.9 subdir-objects tar-ustar no-dist-gzip dist-bzip2]) +AM_MAINTAINER_MODE + +AC_CONFIG_HEADERS(config.h) + +dnl Required programs +AC_PROG_CC +AM_PROG_CC_C_O +AC_PROG_INSTALL +AC_PROG_LIBTOOL + +PKG_CHECK_MODULES(MM, dbus-glib-1 >= 0.75 glib-2.0 >= 2.18 gmodule-2.0 gobject-2.0) + +PKG_CHECK_MODULES(GUDEV, gudev-1.0) +AC_SUBST(GUDEV_CFLAGS) +AC_SUBST(GUDEV_LIBS) + +AC_ARG_WITH(dbus-sys-dir, AS_HELP_STRING([--with-dbus-sys-dir=DIR], [where D-BUS system.d directory is])) + +if test -n "$with_dbus_sys_dir" ; then + DBUS_SYS_DIR="$with_dbus_sys_dir" +else + DBUS_SYS_DIR="${sysconfdir}/dbus-1/system.d" +fi +AC_SUBST(DBUS_SYS_DIR) + +AC_ARG_WITH(udev-base-dir, AS_HELP_STRING([--with-udev-base-dir=DIR], [where udev base directory is])) + +if test -n "$with_udev_base_dir" ; then + UDEV_BASE_DIR="$with_udev_base_dir" +else + UDEV_BASE_DIR="/lib/udev" +fi +AC_SUBST(UDEV_BASE_DIR) + +GLIB_GENMARSHAL=`pkg-config --variable=glib_genmarshal glib-2.0` +AC_SUBST(GLIB_GENMARSHAL) + +# PPPD +AC_CHECK_HEADERS(pppd/pppd.h, have_pppd_headers="yes", have_pppd_headers="no") +AM_CONDITIONAL(HAVE_PPPD_H, test "x$have_pppd_headers" = "xyes") +case $have_pppd_headers in + yes) ;; + *) + have_pppd_headers=no + ;; +esac + +AC_ARG_WITH([pppd-plugin-dir], AS_HELP_STRING([--with-pppd-plugin-dir=DIR], [path to the pppd plugins directory])) + +if test -n "$with_pppd_plugin_dir" ; then + PPPD_PLUGIN_DIR="$with_pppd_plugin_dir" +else + PPPD_PLUGIN_DIR="${libdir}/pppd/2.4.4" +fi +AC_SUBST(PPPD_PLUGIN_DIR) + +AC_ARG_ENABLE(more-warnings, +AS_HELP_STRING([--enable-more-warnings], [Maximum compiler warnings]), set_more_warnings="$enableval",set_more_warnings=yes) +AC_MSG_CHECKING(for more warnings, including -Werror) +if test "$GCC" = "yes" -a "$set_more_warnings" != "no"; then + AC_MSG_RESULT(yes) + CFLAGS="-Wall -Werror -std=gnu89 $CFLAGS" + + for option in -Wshadow -Wmissing-declarations -Wmissing-prototypes \ + -Wdeclaration-after-statement -Wstrict-prototypes \ + -Wfloat-equal -Wno-unused-parameter -Wno-sign-compare \ + -fno-strict-aliasing; do + SAVE_CFLAGS="$CFLAGS" + CFLAGS="$CFLAGS $option" + AC_MSG_CHECKING([whether gcc understands $option]) + AC_TRY_COMPILE([], [], + has_option=yes, + has_option=no) + if test $has_option = no; then + CFLAGS="$SAVE_CFLAGS" + fi + AC_MSG_RESULT($has_option) + unset has_option + unset SAVE_CFLAGS + done + unset option +else + AC_MSG_RESULT(no) +fi + +AC_ARG_WITH(docs, AC_HELP_STRING([--with-docs], [Build ModemManager documentation])) +AM_CONDITIONAL(WITH_DOCS, test "x$with_docs" = "xyes") +case $with_docs in + yes) ;; + *) + with_docs=no + ;; +esac + +dnl +dnl Tests +dnl +AC_ARG_WITH(tests, AS_HELP_STRING([--with-tests], [Build ModemManager tests])) +AM_CONDITIONAL(WITH_TESTS, test "x$with_tests" = "xyes") +case $with_tests in + yes) + with_tests=yes + ;; + *) + with_tests=no + ;; +esac + +AC_CONFIG_FILES([ +Makefile +marshallers/Makefile +src/Makefile +src/tests/Makefile +plugins/Makefile +test/Makefile +introspection/Makefile +]) +AC_OUTPUT + +echo +echo Building with D-Bus system directory: ${DBUS_SYS_DIR} +echo +echo Building with udev base directory: ${UDEV_BASE_DIR} +echo +echo Building documentation: ${with_docs} +echo +echo Building PPP-enabled tests: ${have_pppd_headers} +echo + diff --git a/doc-generator.xsl b/doc-generator.xsl new file mode 100644 index 0000000..bcc88e8 --- /dev/null +++ b/doc-generator.xsl @@ -0,0 +1,691 @@ +<!-- Generate HTML documentation from the Telepathy specification. +The master copy of this stylesheet is in the Telepathy spec repository - +please make any changes there. + +Copyright (C) 2006, 2007 Collabora Limited + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +--> + +<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" + xmlns:tp="http://telepathy.freedesktop.org/wiki/DbusSpec#extensions-v0" + exclude-result-prefixes="tp"> + <!--Don't move the declaration of the HTML namespace up here - XMLNSs + don't work ideally in the presence of two things that want to use the + absence of a prefix, sadly. --> + + <xsl:template match="*" mode="identity"> + <xsl:copy> + <xsl:apply-templates mode="identity"/> + </xsl:copy> + </xsl:template> + + <xsl:template match="tp:docstring"> + <xsl:apply-templates select="node()" mode="identity"/> + </xsl:template> + + <xsl:template match="tp:errors"> + <h1 xmlns="http://www.w3.org/1999/xhtml">Errors:</h1> + <xsl:apply-templates/> + </xsl:template> + + <xsl:template match="tp:generic-types"> + <h1 xmlns="http://www.w3.org/1999/xhtml">Generic types:</h1> + <xsl:call-template name="do-types"/> + </xsl:template> + + <xsl:template name="do-types"> + <xsl:if test="tp:simple-type"> + <h2 xmlns="http://www.w3.org/1999/xhtml">Simple types:</h2> + <xsl:apply-templates select="tp:simple-type"/> + </xsl:if> + + <xsl:if test="tp:enum"> + <h2 xmlns="http://www.w3.org/1999/xhtml">Enumerated types:</h2> + <xsl:apply-templates select="tp:enum"/> + </xsl:if> + + <xsl:if test="tp:flags"> + <h2 xmlns="http://www.w3.org/1999/xhtml">Sets of flags:</h2> + <xsl:apply-templates select="tp:flags"/> + </xsl:if> + + <xsl:if test="tp:struct"> + <h2 xmlns="http://www.w3.org/1999/xhtml">Structure types:</h2> + <xsl:apply-templates select="tp:struct"/> + </xsl:if> + + <xsl:if test="tp:mapping"> + <h2 xmlns="http://www.w3.org/1999/xhtml">Mapping types:</h2> + <xsl:apply-templates select="tp:mapping"/> + </xsl:if> + + <xsl:if test="tp:external-type"> + <h2 xmlns="http://www.w3.org/1999/xhtml">Types defined elsewhere:</h2> + <dl><xsl:apply-templates select="tp:external-type"/></dl> + </xsl:if> + </xsl:template> + + <xsl:template match="tp:error"> + <h2 xmlns="http://www.w3.org/1999/xhtml"><a name="{concat(../@namespace, '.', translate(@name, ' ', ''))}"></a><xsl:value-of select="concat(../@namespace, '.', translate(@name, ' ', ''))"/></h2> + <xsl:apply-templates select="tp:docstring"/> + </xsl:template> + + <xsl:template match="/tp:spec/tp:copyright"> + <div xmlns="http://www.w3.org/1999/xhtml"> + <xsl:apply-templates/> + </div> + </xsl:template> + <xsl:template match="/tp:spec/tp:license"> + <div xmlns="http://www.w3.org/1999/xhtml" class="license"> + <xsl:apply-templates mode="identity"/> + </div> + </xsl:template> + + <xsl:template match="tp:copyright"/> + <xsl:template match="tp:license"/> + + <xsl:template match="interface"> + <h1 xmlns="http://www.w3.org/1999/xhtml"><a name="{@name}"></a><xsl:value-of select="@name"/></h1> + + <xsl:if test="@tp:causes-havoc"> + <p xmlns="http://www.w3.org/1999/xhtml" class="causes-havoc"> + This interface is <xsl:value-of select="@tp:causes-havoc"/> + and is likely to cause havoc to your API/ABI if bindings are generated. + Don't include it in libraries that care about compatibility. + </p> + </xsl:if> + + <xsl:if test="tp:requires"> + <p>Implementations of this interface must also implement:</p> + <ul xmlns="http://www.w3.org/1999/xhtml"> + <xsl:for-each select="tp:requires"> + <li><code><a href="#{@interface}"><xsl:value-of select="@interface"/></a></code></li> + </xsl:for-each> + </ul> + </xsl:if> + + <xsl:apply-templates select="tp:docstring" /> + + <xsl:choose> + <xsl:when test="method"> + <h2 xmlns="http://www.w3.org/1999/xhtml">Methods:</h2> + <xsl:apply-templates select="method"/> + </xsl:when> + <xsl:otherwise> + <p xmlns="http://www.w3.org/1999/xhtml">Interface has no methods.</p> + </xsl:otherwise> + </xsl:choose> + + <xsl:choose> + <xsl:when test="signal"> + <h2 xmlns="http://www.w3.org/1999/xhtml">Signals:</h2> + <xsl:apply-templates select="signal"/> + </xsl:when> + <xsl:otherwise> + <p xmlns="http://www.w3.org/1999/xhtml">Interface has no signals.</p> + </xsl:otherwise> + </xsl:choose> + + <xsl:choose> + <xsl:when test="property"> + <h2 xmlns="http://www.w3.org/1999/xhtml">Properties:</h2> + <dl xmlns="http://www.w3.org/1999/xhtml"> + <xsl:apply-templates select="property"/> + </dl> + </xsl:when> + <xsl:otherwise> + <p xmlns="http://www.w3.org/1999/xhtml">Interface has no properties.</p> + </xsl:otherwise> + </xsl:choose> + + <xsl:call-template name="do-types"/> + + </xsl:template> + + <xsl:template match="tp:flags"> + <h3> + <a name="type-{@name}"> + <xsl:value-of select="@name"/> + </a> + </h3> + <xsl:apply-templates select="tp:docstring" /> + <dl xmlns="http://www.w3.org/1999/xhtml"> + <xsl:variable name="value-prefix"> + <xsl:choose> + <xsl:when test="@value-prefix"> + <xsl:value-of select="@value-prefix"/> + </xsl:when> + <xsl:otherwise> + <xsl:value-of select="@name"/> + </xsl:otherwise> + </xsl:choose> + </xsl:variable> + <xsl:for-each select="tp:flag"> + <dt xmlns="http://www.w3.org/1999/xhtml"><code><xsl:value-of select="concat($value-prefix, '_', @suffix)"/> = <xsl:value-of select="@value"/></code></dt> + <xsl:choose> + <xsl:when test="tp:docstring"> + <dd xmlns="http://www.w3.org/1999/xhtml"><xsl:apply-templates select="tp:docstring" /></dd> + </xsl:when> + <xsl:otherwise> + <dd xmlns="http://www.w3.org/1999/xhtml">(Undocumented)</dd> + </xsl:otherwise> + </xsl:choose> + </xsl:for-each> + </dl> + </xsl:template> + + <xsl:template match="tp:enum"> + <h3 xmlns="http://www.w3.org/1999/xhtml"> + <a name="type-{@name}"> + <xsl:value-of select="@name"/> + </a> + </h3> + <xsl:apply-templates select="tp:docstring" /> + <dl xmlns="http://www.w3.org/1999/xhtml"> + <xsl:variable name="value-prefix"> + <xsl:choose> + <xsl:when test="@value-prefix"> + <xsl:value-of select="@value-prefix"/> + </xsl:when> + <xsl:otherwise> + <xsl:value-of select="@name"/> + </xsl:otherwise> + </xsl:choose> + </xsl:variable> + <xsl:for-each select="tp:enumvalue"> + <dt xmlns="http://www.w3.org/1999/xhtml"><code><xsl:value-of select="concat($value-prefix, '_', @suffix)"/> = <xsl:value-of select="@value"/></code></dt> + <xsl:choose> + <xsl:when test="tp:docstring"> + <dd xmlns="http://www.w3.org/1999/xhtml"><xsl:apply-templates select="tp:docstring" /></dd> + </xsl:when> + <xsl:otherwise> + <dd xmlns="http://www.w3.org/1999/xhtml">(Undocumented)</dd> + </xsl:otherwise> + </xsl:choose> + </xsl:for-each> + </dl> + </xsl:template> + + <xsl:template match="property"> + <dt xmlns="http://www.w3.org/1999/xhtml"> + <xsl:if test="@name"> + <code><xsl:value-of select="@name"/></code> - + </xsl:if> + <code><xsl:value-of select="@type"/></code> - + <code>(<xsl:value-of select="@access"/>)</code> + <xsl:call-template name="parenthesized-tp-type"/> + </dt> + <dd xmlns="http://www.w3.org/1999/xhtml"> + <xsl:apply-templates select="tp:docstring"/> + </dd> + </xsl:template> + + <xsl:template match="tp:mapping"> + <div xmlns="http://www.w3.org/1999/xhtml" class="struct"> + <h3> + <a name="type-{@name}"> + <xsl:value-of select="@name"/> + </a> - a{ + <xsl:for-each select="tp:member"> + <xsl:value-of select="@type"/> + <xsl:text>: </xsl:text> + <xsl:value-of select="@name"/> + <xsl:if test="position() != last()"> → </xsl:if> + </xsl:for-each> + } + </h3> + <div class="docstring"> + <xsl:apply-templates select="tp:docstring"/> + </div> + <div> + <h4>Members</h4> + <dl> + <xsl:apply-templates select="tp:member" mode="members-in-docstring"/> + </dl> + </div> + </div> + </xsl:template> + + <xsl:template match="tp:docstring" mode="in-index"/> + + <xsl:template match="tp:simple-type | tp:enum | tp:flags | tp:external-type" + mode="in-index"> + - <xsl:value-of select="@type"/> + </xsl:template> + + <xsl:template match="tp:simple-type"> + <div xmlns="http://www.w3.org/1999/xhtml" class="simple-type"> + <h3> + <a name="type-{@name}"> + <xsl:value-of select="@name"/> + </a> - <xsl:value-of select="@type"/> + </h3> + <div class="docstring"> + <xsl:apply-templates select="tp:docstring"/> + </div> + </div> + </xsl:template> + + <xsl:template match="tp:external-type"> + <div xmlns="http://www.w3.org/1999/xhtml" class="external-type"> + <dt> + <a name="type-{@name}"> + <xsl:value-of select="@name"/> + </a> - <xsl:value-of select="@type"/> + </dt> + <dd>Defined by: <xsl:value-of select="@from"/></dd> + </div> + </xsl:template> + + <xsl:template match="tp:struct" mode="in-index"> + - ( <xsl:for-each select="tp:member"> + <xsl:value-of select="@type"/> + <xsl:if test="position() != last()">, </xsl:if> + </xsl:for-each> ) + </xsl:template> + + <xsl:template match="tp:mapping" mode="in-index"> + - a{ <xsl:for-each select="tp:member"> + <xsl:value-of select="@type"/> + <xsl:if test="position() != last()"> → </xsl:if> + </xsl:for-each> } + </xsl:template> + + <xsl:template match="tp:struct"> + <div xmlns="http://www.w3.org/1999/xhtml" class="struct"> + <h3> + <a name="type-{@name}"> + <xsl:value-of select="@name"/> + </a> - ( + <xsl:for-each select="tp:member"> + <xsl:value-of select="@type"/> + <xsl:text>: </xsl:text> + <xsl:value-of select="@name"/> + <xsl:if test="position() != last()">, </xsl:if> + </xsl:for-each> + ) + </h3> + <div class="docstring"> + <xsl:apply-templates select="tp:docstring"/> + </div> + <xsl:choose> + <xsl:when test="string(@array-name) != ''"> + <p>In bindings that need a separate name, arrays of + <xsl:value-of select="@name"/> should be called + <xsl:value-of select="@array-name"/>.</p> + </xsl:when> + <xsl:otherwise> + <p>Arrays of <xsl:value-of select="@name"/> don't generally + make sense.</p> + </xsl:otherwise> + </xsl:choose> + <div> + <h4>Members</h4> + <dl> + <xsl:apply-templates select="tp:member" mode="members-in-docstring"/> + </dl> + </div> + </div> + </xsl:template> + + <xsl:template match="method"> + <div xmlns="http://www.w3.org/1999/xhtml" class="method"> + <h3 xmlns="http://www.w3.org/1999/xhtml"> + <a name="{concat(../@name, concat('.', @name))}"> + <xsl:value-of select="@name"/> + </a> ( + <xsl:for-each xmlns="" select="arg[@direction='in']"> + <xsl:value-of select="@type"/>: <xsl:value-of select="@name"/> + <xsl:if test="position() != last()">, </xsl:if> + </xsl:for-each> + ) → + <xsl:choose> + <xsl:when test="arg[@direction='out']"> + <xsl:for-each xmlns="" select="arg[@direction='out']"> + <xsl:value-of select="@type"/> + <xsl:if test="position() != last()">, </xsl:if> + </xsl:for-each> + </xsl:when> + <xsl:otherwise>nothing</xsl:otherwise> + </xsl:choose> + </h3> + <div xmlns="http://www.w3.org/1999/xhtml" class="docstring"> + <xsl:apply-templates select="tp:docstring" /> + </div> + + <xsl:if test="arg[@direction='in']"> + <div xmlns="http://www.w3.org/1999/xhtml"> + <h4>Parameters</h4> + <dl xmlns="http://www.w3.org/1999/xhtml"> + <xsl:apply-templates select="arg[@direction='in']" + mode="parameters-in-docstring"/> + </dl> + </div> + </xsl:if> + + <xsl:if test="arg[@direction='out']"> + <div xmlns="http://www.w3.org/1999/xhtml"> + <h4>Returns</h4> + <dl xmlns="http://www.w3.org/1999/xhtml"> + <xsl:apply-templates select="arg[@direction='out']" + mode="returns-in-docstring"/> + </dl> + </div> + </xsl:if> + + <xsl:if test="tp:possible-errors"> + <div xmlns="http://www.w3.org/1999/xhtml"> + <h4>Possible errors</h4> + <dl xmlns="http://www.w3.org/1999/xhtml"> + <xsl:apply-templates select="tp:possible-errors/tp:error"/> + </dl> + </div> + </xsl:if> + + </div> + </xsl:template> + + <xsl:template name="parenthesized-tp-type"> + <xsl:if test="@tp:type"> + <xsl:variable name="tp-type" select="@tp:type"/> + <xsl:variable name="single-type"> + <xsl:choose> + <xsl:when test="contains($tp-type, '[]')"> + <xsl:value-of select="substring-before($tp-type, '[]')"/> + </xsl:when> + <xsl:otherwise> + <xsl:value-of select="$tp-type"/> + </xsl:otherwise> + </xsl:choose> + </xsl:variable> + <xsl:choose> + <xsl:when test="//tp:simple-type[@name=$tp-type]" /> + <xsl:when test="//tp:simple-type[concat(@name, '[]')=$tp-type]" /> + <xsl:when test="//tp:struct[concat(@name, '[]')=$tp-type][string(@array-name) != '']" /> + <xsl:when test="//tp:struct[@name=$tp-type]" /> + <xsl:when test="//tp:enum[@name=$tp-type]" /> + <xsl:when test="//tp:enum[concat(@name, '[]')=$tp-type]" /> + <xsl:when test="//tp:flags[@name=$tp-type]" /> + <xsl:when test="//tp:flags[concat(@name, '[]')=$tp-type]" /> + <xsl:when test="//tp:mapping[@name=$tp-type]" /> + <xsl:when test="//tp:external-type[concat(@name, '[]')=$tp-type]" /> + <xsl:when test="//tp:external-type[@name=$tp-type]" /> + <xsl:otherwise> + <xsl:message terminate="yes"> + <xsl:text>ERR: Unable to find type '</xsl:text> + <xsl:value-of select="$tp-type"/> + <xsl:text>' </xsl:text> + </xsl:message> + </xsl:otherwise> + </xsl:choose> + (<a href="#type-{$single-type}"><xsl:value-of select="$tp-type"/></a>) + </xsl:if> + </xsl:template> + + <xsl:template match="tp:member" mode="members-in-docstring"> + <dt xmlns="http://www.w3.org/1999/xhtml"> + <code><xsl:value-of select="@name"/></code> - + <code><xsl:value-of select="@type"/></code> + <xsl:call-template name="parenthesized-tp-type"/> + </dt> + <dd xmlns="http://www.w3.org/1999/xhtml"> + <xsl:choose> + <xsl:when test="tp:docstring"> + <xsl:apply-templates select="tp:docstring" /> + </xsl:when> + <xsl:otherwise> + <em>(undocumented)</em> + </xsl:otherwise> + </xsl:choose> + </dd> + </xsl:template> + + <xsl:template match="arg" mode="parameters-in-docstring"> + <dt xmlns="http://www.w3.org/1999/xhtml"> + <code><xsl:value-of select="@name"/></code> - + <code><xsl:value-of select="@type"/></code> + <xsl:call-template name="parenthesized-tp-type"/> + </dt> + <dd xmlns="http://www.w3.org/1999/xhtml"> + <xsl:apply-templates select="tp:docstring" /> + </dd> + </xsl:template> + + <xsl:template match="arg" mode="returns-in-docstring"> + <dt xmlns="http://www.w3.org/1999/xhtml"> + <xsl:if test="@name"> + <code><xsl:value-of select="@name"/></code> - + </xsl:if> + <code><xsl:value-of select="@type"/></code> + <xsl:call-template name="parenthesized-tp-type"/> + </dt> + <dd xmlns="http://www.w3.org/1999/xhtml"> + <xsl:apply-templates select="tp:docstring"/> + </dd> + </xsl:template> + + <xsl:template match="tp:possible-errors/tp:error"> + <dt xmlns="http://www.w3.org/1999/xhtml"> + <code><xsl:value-of select="@name"/></code> + </dt> + <dd xmlns="http://www.w3.org/1999/xhtml"> + <xsl:variable name="name" select="@name"/> + <xsl:choose> + <xsl:when test="tp:docstring"> + <xsl:apply-templates select="tp:docstring"/> + </xsl:when> + <xsl:when test="//tp:errors/tp:error[concat(../@namespace, '.', translate(@name, ' ', ''))=$name]/tp:docstring"> + <xsl:apply-templates select="//tp:errors/tp:error[concat(../@namespace, '.', translate(@name, ' ', ''))=$name]/tp:docstring"/> <em xmlns="http://www.w3.org/1999/xhtml">(generic description)</em> + </xsl:when> + <xsl:otherwise> + (Undocumented.) + </xsl:otherwise> + </xsl:choose> + </dd> + </xsl:template> + + <xsl:template match="signal"> + <div xmlns="http://www.w3.org/1999/xhtml" class="signal"> + <h3 xmlns="http://www.w3.org/1999/xhtml"> + <a name="{concat(../@name, concat('.', @name))}"> + <xsl:value-of select="@name"/> + </a> ( + <xsl:for-each xmlns="" select="arg"> + <xsl:value-of select="@type"/>: <xsl:value-of select="@name"/> + <xsl:if test="position() != last()">, </xsl:if> + </xsl:for-each> + )</h3> + <div xmlns="http://www.w3.org/1999/xhtml" class="docstring"> + <xsl:apply-templates select="tp:docstring"/> + </div> + + <xsl:if test="arg"> + <div xmlns="http://www.w3.org/1999/xhtml"> + <h4>Parameters</h4> + <dl xmlns="http://www.w3.org/1999/xhtml"> + <xsl:apply-templates select="arg" mode="parameters-in-docstring"/> + </dl> + </div> + </xsl:if> + </div> + </xsl:template> + + <xsl:output method="xml" indent="no" encoding="ascii" + omit-xml-declaration="yes" + doctype-system="http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd" + doctype-public="-//W3C//DTD XHTML 1.0 Strict//EN" /> + + <xsl:template match="/tp:spec"> + <html xmlns="http://www.w3.org/1999/xhtml"> + <head> + <title> + <xsl:value-of select="tp:title"/> + <xsl:if test="tp:version"> + <xsl:text> version </xsl:text> + <xsl:value-of select="tp:version"/> + </xsl:if> + </title> + <style type="text/css"> + + body { + font-family: sans-serif; + margin: 2em; + height: 100%; + font-size: 1.2em; + } + h1 { + padding-top: 5px; + padding-bottom: 5px; + font-size: 1.6em; + background: #dadae2; + } + h2 { + font-size: 1.3em; + } + h3 { + font-size: 1.2em; + } + a:link, a:visited, a:link:hover, a:visited:hover { + font-weight: bold; + } + .topbox { + padding-top: 10px; + padding-left: 10px; + border-bottom: black solid 1px; + padding-bottom: 10px; + background: #dadae2; + font-size: 2em; + font-weight: bold; + color: #5c5c5c; + } + .topnavbox { + padding-left: 10px; + padding-top: 5px; + padding-bottom: 5px; + background: #abacba; + border-bottom: black solid 1px; + font-size: 1.2em; + } + .topnavbox a{ + color: black; + font-weight: normal; + } + .sidebar { + float: left; + /* width:9em; + border-right:#abacba solid 1px; + border-left: #abacba solid 1px; + height:100%; */ + border: #abacba solid 1px; + padding-left: 10px; + margin-left: 10px; + padding-right: 10px; + margin-right: 10px; + color: #5d5d5d; + background: #dadae2; + } + .sidebar a { + text-decoration: none; + border-bottom: #e29625 dotted 1px; + color: #e29625; + font-weight: normal; + } + .sidebar h1 { + font-size: 1.2em; + color: black; + } + .sidebar ul { + padding-left: 25px; + padding-bottom: 10px; + border-bottom: #abacba solid 1px; + } + .sidebar li { + padding-top: 2px; + padding-bottom: 2px; + } + .sidebar h2 { + font-style:italic; + font-size: 0.81em; + padding-left: 5px; + padding-right: 5px; + font-weight: normal; + } + .date { + font-size: 0.6em; + float: right; + font-style: italic; + } + .method { + margin-left: 1em; + margin-right: 4em; + } + .signal { + margin-left: 1em; + margin-right: 4em; + } + + </style> + </head> + <body> + <h1 class="topbox"> + <xsl:value-of select="tp:title" /> + </h1> + <xsl:if test="tp:version"> + <h2>Version <xsl:apply-templates select="tp:version"/></h2> + </xsl:if> + <xsl:apply-templates select="tp:copyright"/> + <xsl:apply-templates select="tp:license"/> + <xsl:apply-templates select="tp:docstring"/> + + <h2>Interfaces</h2> + <ul> + <xsl:for-each select="node/interface"> + <li><code><a href="#{@name}"><xsl:value-of select="@name"/></a></code></li> + </xsl:for-each> + </ul> + + <xsl:apply-templates select="node"/> + <xsl:apply-templates select="tp:generic-types"/> + <xsl:apply-templates select="tp:errors"/> + + <h1>Index</h1> + <h2>Index of interfaces</h2> + <ul> + <xsl:for-each select="node/interface"> + <li><code><a href="#{@name}"><xsl:value-of select="@name"/></a></code></li> + </xsl:for-each> + </ul> + <h2>Index of types</h2> + <ul> + <xsl:for-each select="//tp:simple-type | //tp:enum | //tp:flags | //tp:mapping | //tp:struct | //tp:external-type"> + <xsl:sort select="@name"/> + <li> + <code> + <a href="#type-{@name}"> + <xsl:value-of select="@name"/> + </a> + </code> + <xsl:apply-templates mode="in-index" select="."/> + </li> + </xsl:for-each> + </ul> + </body> + </html> + </xsl:template> + +</xsl:stylesheet> + +<!-- vim:set sw=2 sts=2 et: --> diff --git a/docs/plugins.txt b/docs/plugins.txt new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/docs/plugins.txt diff --git a/introspection/Makefile.am b/introspection/Makefile.am new file mode 100644 index 0000000..404c1d9 --- /dev/null +++ b/introspection/Makefile.am @@ -0,0 +1,16 @@ +EXTRA_DIST = \ + all.xml \ + mm-manager.xml \ + mm-mobile-error.xml \ + mm-modem.xml \ + mm-modem-cdma.xml \ + mm-modem-connect-error.xml \ + mm-modem-error.xml \ + mm-modem-gsm.xml \ + mm-modem-gsm-card.xml \ + mm-modem-gsm-contacts.xml \ + mm-modem-gsm-hso.xml \ + mm-modem-gsm-network.xml \ + mm-modem-gsm-sms.xml \ + mm-modem-simple.xml \ + mm-serial-error.xml diff --git a/introspection/all.xml b/introspection/all.xml new file mode 100644 index 0000000..426baf0 --- /dev/null +++ b/introspection/all.xml @@ -0,0 +1,40 @@ +<tp:spec + xmlns:tp="http://telepathy.freedesktop.org/wiki/DbusSpec#extensions-v0" + xmlns:xi="http://www.w3.org/2001/XInclude"> + + <tp:title>ModemManager D-Bus Interface Specification</tp:title> + <tp:version>0.1</tp:version> + <tp:copyright>Copyright (C) 2008 Novell, Inc.</tp:copyright> + + <tp:license xmlns="http://www.w3.org/1999/xhtml"> + <p>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.</p> + + <p>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.</p> + + <p>You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.</p> + </tp:license> + + <xi:include href="mm-manager.xml"/> + <xi:include href="mm-modem.xml"/> + <xi:include href="mm-modem-simple.xml"/> + <xi:include href="mm-modem-cdma.xml"/> + <xi:include href="mm-modem-gsm.xml"/> + <xi:include href="mm-modem-gsm-card.xml"/> + <xi:include href="mm-modem-gsm-contacts.xml"/> + <xi:include href="mm-modem-gsm-network.xml"/> + <xi:include href="mm-modem-gsm-sms.xml"/> + <xi:include href="mm-modem-gsm-hso.xml"/> + + <xi:include href="mm-serial-error.xml"/> + <xi:include href="mm-modem-error.xml"/> + <xi:include href="mm-modem-connect-error.xml"/> + <xi:include href="mm-mobile-error.xml"/> +</tp:spec> diff --git a/introspection/mm-manager.xml b/introspection/mm-manager.xml new file mode 100644 index 0000000..bdeac01 --- /dev/null +++ b/introspection/mm-manager.xml @@ -0,0 +1,40 @@ +<?xml version="1.0" encoding="UTF-8" ?> + +<node name="/" xmlns:tp="http://telepathy.freedesktop.org/wiki/DbusSpec#extensions-v0"> + <interface name="org.freedesktop.ModemManager"> + <method name="EnumerateDevices"> + <tp:docstring> + Get the list of modem devices. + </tp:docstring> + <annotation name="org.freedesktop.DBus.GLib.CSymbol" value="impl_manager_enumerate_devices"/> + <arg name="devices" type="ao" direction="out"> + <tp:docstring> + List of object paths of modem devices known to the system. + </tp:docstring> + </arg> + </method> + + <signal name="DeviceAdded"> + <tp:docstring> + A device was added to the system. + </tp:docstring> + <arg name="device" type="o"> + <tp:docstring> + The object path of the newly added device. + </tp:docstring> + </arg> + </signal> + + <signal name="DeviceRemoved"> + <tp:docstring> + A device was removed from the system, and is no longer available. + </tp:docstring> + <arg name="device" type="o"> + <tp:docstring> + The object path of the device that was just removed. + </tp:docstring> + </arg> + </signal> + + </interface> +</node> diff --git a/introspection/mm-mobile-error.xml b/introspection/mm-mobile-error.xml new file mode 100644 index 0000000..5edbcb7 --- /dev/null +++ b/introspection/mm-mobile-error.xml @@ -0,0 +1,318 @@ +<?xml version="1.0" ?> +<tp:errors xmlns:tp="http://telepathy.freedesktop.org/wiki/DbusSpec#extensions-v0" + namespace="org.freedesktop.ModemManager.Modem.Gsm"> + <tp:copyright>Copyright (C) 2008 Novell, Inc.</tp:copyright> + + <tp:error name="PhoneFailure"> + <tp:docstring> + A phone failure. + </tp:docstring> + </tp:error> + + <tp:error name="NoConnection"> + <tp:docstring> + No connection to phone. + </tp:docstring> + </tp:error> + + <tp:error name="LinkReserved"> + <tp:docstring> + Phone-adaptor link reserved. + </tp:docstring> + </tp:error> + + <tp:error name="OperationNotAllowed"> + <tp:docstring> + Operation not allowed. + </tp:docstring> + </tp:error> + + <tp:error name="OperationNotSupported"> + <tp:docstring> + Operation not supported. + </tp:docstring> + </tp:error> + + <tp:error name="PhSimPinRequired"> + <tp:docstring> + PH-SIM PIN required. + </tp:docstring> + </tp:error> + + <tp:error name="PhFSimPinRequired"> + <tp:docstring> + PH-FSIM PIN required. + </tp:docstring> + </tp:error> + + <tp:error name="PhFSimPukRequired"> + <tp:docstring> + PH-FSIM PUK required. + </tp:docstring> + </tp:error> + + <tp:error name="SimNotInserted"> + <tp:docstring> + SIM not inserted. + </tp:docstring> + </tp:error> + + <tp:error name="SimPinRequired"> + <tp:docstring> + SIM PIN required. + </tp:docstring> + </tp:error> + + <tp:error name="SimPukRequired"> + <tp:docstring> + SIM PUK required. + </tp:docstring> + </tp:error> + + <tp:error name="SimFailure"> + <tp:docstring> + SIM failure. + </tp:docstring> + </tp:error> + + <tp:error name="SimBusy"> + <tp:docstring> + SIM busy. + </tp:docstring> + </tp:error> + + <tp:error name="SimWrong"> + <tp:docstring> + SIM wrong. + </tp:docstring> + </tp:error> + + <tp:error name="IncorrectPassword"> + <tp:docstring> + Incorrect password. + </tp:docstring> + </tp:error> + + <tp:error name="SimPin2Required"> + <tp:docstring> + SIM PIN2 required. + </tp:docstring> + </tp:error> + + <tp:error name="SimPuk2Required"> + <tp:docstring> + SIM PUK2 required. + </tp:docstring> + </tp:error> + + <tp:error name="MemoryFull"> + <tp:docstring> + Memory full. + </tp:docstring> + </tp:error> + + <tp:error name="InvalidIndex"> + <tp:docstring> + Invalid index. + </tp:docstring> + </tp:error> + + <tp:error name="NotFound"> + <tp:docstring> + Not found. + </tp:docstring> + </tp:error> + + <tp:error name="MemoryFailure"> + <tp:docstring> + Memory failure. + </tp:docstring> + </tp:error> + + <tp:error name="TextTooLong"> + <tp:docstring> + Text string too long. + </tp:docstring> + </tp:error> + + <tp:error name="InvalidChars"> + <tp:docstring> + Invalid characters in text string. + </tp:docstring> + </tp:error> + + <tp:error name="DialStringTooLong"> + <tp:docstring> + Dial string too long. + </tp:docstring> + </tp:error> + + <tp:error name="InvalidDialString"> + <tp:docstring> + Invalid characters in dial string. + </tp:docstring> + </tp:error> + + <tp:error name="NoNetwork"> + <tp:docstring> + No network service. + </tp:docstring> + </tp:error> + + <tp:error name="NetworkTimeout"> + <tp:docstring> + Network timeout. + </tp:docstring> + </tp:error> + + <tp:error name="NetworkNotAllowed"> + <tp:docstring> + Network not allowed - emergency calls only. + </tp:docstring> + </tp:error> + + <tp:error name="NetworkPinRequired"> + <tp:docstring> + Network personalization PIN required. + </tp:docstring> + </tp:error> + + <tp:error name="NetworkPukRequired"> + <tp:docstring> + Network personalization PUK required. + </tp:docstring> + </tp:error> + + <tp:error name="NetworkSubsetPinRequired"> + <tp:docstring> + Network subset personalization PIN required. + </tp:docstring> + </tp:error> + + <tp:error name="NetworkSubsetPukRequired"> + <tp:docstring> + Network subset personalization PUK required. + </tp:docstring> + </tp:error> + + <tp:error name="ServicePinRequired"> + <tp:docstring> + Service provider personalization PIN required. + </tp:docstring> + </tp:error> + + <tp:error name="ServicePukRequired"> + <tp:docstring> + Service provider personalization PUK required. + </tp:docstring> + </tp:error> + + <tp:error name="CorporatePinRequired"> + <tp:docstring> + Corporate personalization PIN required. + </tp:docstring> + </tp:error> + + <tp:error name="CorporatePukRequired"> + <tp:docstring> + Corporate personalization PUK required. + </tp:docstring> + </tp:error> + + <tp:error name="HiddenKeyRequired"> + <tp:docstring> + Hidden key required. This key is required when accessing hidden phonebook entries. + </tp:docstring> + </tp:error> + + <tp:error name="EapMethodNotSupported"> + <tp:docstring> + EAP method not supported. + </tp:docstring> + </tp:error> + + <tp:error name="IncorrectParams"> + <tp:docstring> + Incorrect parameters. + </tp:docstring> + </tp:error> + + <tp:error name="Unknown"> + <tp:docstring> + An unknown error. + </tp:docstring> + </tp:error> + + <tp:error name="GprsIllegalMs"> + <tp:docstring> + Illegal MS. + </tp:docstring> + </tp:error> + + <tp:error name="GprsIllegalMe"> + <tp:docstring> + Illegal ME. + </tp:docstring> + </tp:error> + + <tp:error name="GprsServiceNotAllowed"> + <tp:docstring> + GPRS services not allowed. + </tp:docstring> + </tp:error> + + <tp:error name="GprsPlmnNotAllowed"> + <tp:docstring> + PLMN not allowed. + </tp:docstring> + </tp:error> + + <tp:error name="GprsLocationNotAllowed"> + <tp:docstring> + Location area not allowed. + </tp:docstring> + </tp:error> + + <tp:error name="GprsRoamingNotAllowed"> + <tp:docstring> + Roaming not allowed in this location area. + </tp:docstring> + </tp:error> + + <tp:error name="GprsOptionNotSupported"> + <tp:docstring> + Service option not supported. + </tp:docstring> + </tp:error> + + <tp:error name="GprsNotSubscribed"> + <tp:docstring> + Requested service option not subscribed. + </tp:docstring> + </tp:error> + + <tp:error name="GprsOutOfOrder"> + <tp:docstring> + Service option temporarily out of order. + </tp:docstring> + </tp:error> + + <tp:error name="GprsPdpAuthFailure"> + <tp:docstring> + PDP authentication failure. + </tp:docstring> + </tp:error> + + <tp:error name="GprsUnspecified"> + <tp:docstring> + Unspecified GPRS error + </tp:docstring> + </tp:error> + + <tp:error name="GprsInvalidClass"> + <tp:docstring> + Invalid mobile class. + </tp:docstring> + </tp:error> + +</tp:errors> diff --git a/introspection/mm-modem-cdma.xml b/introspection/mm-modem-cdma.xml new file mode 100644 index 0000000..e224296 --- /dev/null +++ b/introspection/mm-modem-cdma.xml @@ -0,0 +1,97 @@ +<?xml version="1.0" encoding="UTF-8" ?> + +<node name="/" xmlns:tp="http://telepathy.freedesktop.org/wiki/DbusSpec#extensions-v0"> + <interface name="org.freedesktop.ModemManager.Modem.Cdma"> + + <method name="GetSignalQuality"> + <tp:docstring> + Get the current signal quality. + </tp:docstring> + <annotation name="org.freedesktop.DBus.GLib.Async" value=""/> + <annotation name="org.freedesktop.DBus.GLib.CSymbol" value="impl_modem_cdma_get_signal_quality"/> + <arg name="quality" type="u" direction="out"> + <tp:docstring> + Signal quality (percent). + </tp:docstring> + </arg> + </method> + + <method name="GetEsn"> + <tp:docstring> + Get the Electronic Serial Number of the card. + </tp:docstring> + <annotation name="org.freedesktop.DBus.GLib.Async" value=""/> + <annotation name="org.freedesktop.DBus.GLib.CSymbol" value="impl_modem_cdma_get_esn"/> + <arg name="esn" type="s" direction="out"> + <tp:docstring> + The ESN. + </tp:docstring> + </arg> + </method> + + <method name="GetServingSystem"> + <tp:docstring> + Get the Service System details of the current network, if registered. + </tp:docstring> + <annotation name="org.freedesktop.DBus.GLib.Async" value=""/> + <annotation name="org.freedesktop.DBus.GLib.CSymbol" value="impl_modem_cdma_get_serving_system"/> + <arg name="info" type="(usu)" direction="out"> + <tp:docstring> + A structure containing the Band Class (0 = unknown, 1 = 800 MHz, 2 = 1900 MHz), the Band ("A" - "F" as defined by IS707-A), and the System ID of the serving network. + </tp:docstring> + </arg> + </method> + + <signal name="SignalQuality"> + <tp:docstring> + The signal quality changed. + </tp:docstring> + <arg name="quality" type="u"> + <tp:docstring> + The new quality in percent, 0..100. + </tp:docstring> + </arg> + </signal> + + <method name="GetRegistrationState"> + <tp:docstring>Get device registration state.</tp:docstring> + <annotation name="org.freedesktop.DBus.GLib.Async" value=""/> + <annotation name="org.freedesktop.DBus.GLib.CSymbol" value="impl_modem_cdma_get_registration_state"/> + <arg name="cdma-1x-state" type="u" direction="out" tp:type="MM_MODEM_CDMA_REGISTRATION_STATE"> + <tp:docstring>CDMA 1x registration state.</tp:docstring> + </arg> + <arg name="evdo-state" type="u" direction="out" tp:type="MM_MODEM_CDMA_REGISTRATION_STATE"> + <tp:docstring>EVDO registration state.</tp:docstring> + </arg> + </method> + + <signal name="RegistrationStateChanged"> + <tp:docstring> + The device registration state changed. + </tp:docstring> + <arg name="cdma-1x-state" type="u" tp:type="MM_MODEM_CDMA_REGISTRATION_STATE"> + <tp:docstring>CDMA 1x registration state.</tp:docstring> + </arg> + <arg name="evdo-state" type="u" tp:type="MM_MODEM_CDMA_REGISTRATION_STATE"> + <tp:docstring>EVDO registration state.</tp:docstring> + </arg> + </signal> + + <tp:enum name="MM_MODEM_CDMA_REGISTRATION_STATE" type="u"> + <tp:enumvalue suffix="UNKNOWN" value="0"> + <tp:docstring>Registration status is unknown or the device is not registered.</tp:docstring> + </tp:enumvalue> + <tp:enumvalue suffix="REGISTERED" value="1"> + <tp:docstring>Registered, but roaming status is unknown or cannot be provided by the device. The device may or may not be roaming.</tp:docstring> + </tp:enumvalue> + <tp:enumvalue suffix="HOME" value="2"> + <tp:docstring>Currently registered on the home network.</tp:docstring> + </tp:enumvalue> + <tp:enumvalue suffix="ROAMING" value="3"> + <tp:docstring>Currently registered on a roaming network.</tp:docstring> + </tp:enumvalue> + </tp:enum> + + </interface> +</node> + diff --git a/introspection/mm-modem-connect-error.xml b/introspection/mm-modem-connect-error.xml new file mode 100644 index 0000000..b983702 --- /dev/null +++ b/introspection/mm-modem-connect-error.xml @@ -0,0 +1,30 @@ +<?xml version="1.0" ?> +<tp:errors xmlns:tp="http://telepathy.freedesktop.org/wiki/DbusSpec#extensions-v0" + namespace="org.freedesktop.ModemManager.Modem"> + <tp:copyright>Copyright (C) 2008 Novell, Inc.</tp:copyright> + + <tp:error name="NoCarrier"> + <tp:docstring> + No carrier. + </tp:docstring> + </tp:error> + + <tp:error name="NoDialtone"> + <tp:docstring> + No dialtone. + </tp:docstring> + </tp:error> + + <tp:error name="Busy"> + <tp:docstring> + Busy. + </tp:docstring> + </tp:error> + + <tp:error name="NoAnswer"> + <tp:docstring> + No answer. + </tp:docstring> + </tp:error> + +</tp:errors> diff --git a/introspection/mm-modem-error.xml b/introspection/mm-modem-error.xml new file mode 100644 index 0000000..200e093 --- /dev/null +++ b/introspection/mm-modem-error.xml @@ -0,0 +1,36 @@ +<?xml version="1.0" ?> +<tp:errors xmlns:tp="http://telepathy.freedesktop.org/wiki/DbusSpec#extensions-v0" + namespace="org.freedesktop.ModemManager.Modem"> + <tp:copyright>Copyright (C) 2008 Novell, Inc.</tp:copyright> + + <tp:error name="General"> + <tp:docstring> + A generic error. An example of a generic error is ModemManager not being able to parse the response from modem. + </tp:docstring> + </tp:error> + + <tp:error name="OperationNotSupported"> + <tp:docstring> + Operation not implemented by modem. + </tp:docstring> + </tp:error> + + <tp:error name="Connected"> + <tp:docstring> + Operation could not be performed while the modem is connected. + </tp:docstring> + </tp:error> + + <tp:error name="Disconnected"> + <tp:docstring> + Operation could not be performed while the modem is disconnected. + </tp:docstring> + </tp:error> + + <tp:error name="OperationInProgress"> + <tp:docstring> + Operation could not be performed because it is already in progress. + </tp:docstring> + </tp:error> + +</tp:errors> diff --git a/introspection/mm-modem-gsm-card.xml b/introspection/mm-modem-gsm-card.xml new file mode 100644 index 0000000..ad9348d --- /dev/null +++ b/introspection/mm-modem-gsm-card.xml @@ -0,0 +1,107 @@ +<?xml version="1.0" encoding="UTF-8" ?> + +<node name="/" xmlns:tp="http://telepathy.freedesktop.org/wiki/DbusSpec#extensions-v0"> + <interface name="org.freedesktop.ModemManager.Modem.Gsm.Card"> + <method name="GetImei"> + <tp:docstring> + Get the IMEI of the card. + </tp:docstring> + <annotation name="org.freedesktop.DBus.GLib.Async" value=""/> + <annotation name="org.freedesktop.DBus.GLib.CSymbol" value="impl_gsm_modem_get_imei"/> + <arg name="imei" type="s" direction="out"> + <tp:docstring> + The IMEI. + </tp:docstring> + </arg> + </method> + + <method name="GetImsi"> + <tp:docstring> + Get the IMSI of the SIM card. + </tp:docstring> + <annotation name="org.freedesktop.DBus.GLib.Async" value=""/> + <annotation name="org.freedesktop.DBus.GLib.CSymbol" value="impl_gsm_modem_get_imsi"/> + <arg name="imsi" type="s" direction="out"> + <tp:docstring> + The IMSI. + </tp:docstring> + </arg> + </method> + + <method name="SendPuk"> + <tp:docstring> + Send the PUK and a new PIN to unlock the SIM card. + </tp:docstring> + <annotation name="org.freedesktop.DBus.GLib.Async" value=""/> + <annotation name="org.freedesktop.DBus.GLib.CSymbol" value="impl_gsm_modem_send_puk"/> + <arg name="puk" type="s" direction="in"> + <tp:docstring> + The PUK code. + </tp:docstring> + </arg> + <arg name="pin" type="s" direction="in"> + <tp:docstring> + The PIN code. + </tp:docstring> + </arg> + </method> + + <method name="SendPin"> + <tp:docstring> + Send the PIN (or PUK) to unlock the SIM card. + </tp:docstring> + <annotation name="org.freedesktop.DBus.GLib.Async" value=""/> + <annotation name="org.freedesktop.DBus.GLib.CSymbol" value="impl_gsm_modem_send_pin"/> + <arg name="pin" type="s" direction="in"> + <tp:docstring> + The PIN code. + </tp:docstring> + </arg> + </method> + + <method name="EnablePin"> + <tp:docstring> + Enable or disable the PIN checking. + </tp:docstring> + <annotation name="org.freedesktop.DBus.GLib.Async" value=""/> + <annotation name="org.freedesktop.DBus.GLib.CSymbol" value="impl_gsm_modem_enable_pin"/> + <arg name="pin" type="s" direction="in"> + <tp:docstring> + The PIN code. + </tp:docstring> + </arg> + <arg name="enabled" type="b" direction="in"> + <tp:docstring> + True to enable PIN checking. + </tp:docstring> + </arg> + </method> + + <method name="ChangePin"> + <tp:docstring> + Change the PIN code. + </tp:docstring> + <annotation name="org.freedesktop.DBus.GLib.Async" value=""/> + <annotation name="org.freedesktop.DBus.GLib.CSymbol" value="impl_gsm_modem_change_pin"/> + <arg name="old_pin" type="s" direction="in"> + <tp:docstring> + The current PIN code. + </tp:docstring> + </arg> + <arg name="new_pin" type="s" direction="in"> + <tp:docstring> + The new PIN code. + </tp:docstring> + </arg> + </method> + + <property name="SupportedBands" type="u" access="read" tp:type="MM_MODEM_GSM_BAND"> + <tp:docstring>Bands supported by the card. (Note for plugin writers: returned value must not contain ANY)</tp:docstring> + </property> + + <property name="SupportedModes" type="u" access="read" tp:type="MM_MODEM_GSM_MODE"> + <tp:docstring>Network selection modes supported by the card. (Note for plugin writers: returned value must not contain ANY)</tp:docstring> + </property> + + </interface> +</node> diff --git a/introspection/mm-modem-gsm-contacts.xml b/introspection/mm-modem-gsm-contacts.xml new file mode 100644 index 0000000..60b06ad --- /dev/null +++ b/introspection/mm-modem-gsm-contacts.xml @@ -0,0 +1,104 @@ +<?xml version="1.0" encoding="UTF-8" ?> + +<node name="/" xmlns:tp="http://telepathy.freedesktop.org/wiki/DbusSpec#extensions-v0"> + <interface name="org.freedesktop.ModemManager.Modem.Gsm.Contacts"> + <method name="Add"> + <tp:docstring> + Add a new contact to the SIM card. + </tp:docstring> + <annotation name="org.freedesktop.DBus.GLib.Async" value=""/> + <annotation name="org.freedesktop.DBus.GLib.CSymbol" value="impl_gsm_modem_contacts_add"/> + <arg name="name" type="s" direction="in"> + <tp:docstring> + The name of the contact. + </tp:docstring> + </arg> + <arg name="number" type="s" direction="in"> + <tp:docstring> + The phone number of the contact. + </tp:docstring> + </arg> + <arg name="index" type="u" direction="out"> + <tp:docstring> + The index of the new contact. + </tp:docstring> + </arg> + </method> + + <method name="Delete"> + <tp:docstring> + Delete a contact from the SIM card. + </tp:docstring> + <annotation name="org.freedesktop.DBus.GLib.Async" value=""/> + <annotation name="org.freedesktop.DBus.GLib.CSymbol" value="impl_gsm_modem_contacts_delete"/> + <arg name="index" type="u" direction="in"> + <tp:docstring> + The index of the contact. + </tp:docstring> + </arg> + </method> + + <method name="Get"> + <tp:docstring> + Retrieve a contact from the SIM card. + </tp:docstring> + <annotation name="org.freedesktop.DBus.GLib.Async" value=""/> + <annotation name="org.freedesktop.DBus.GLib.CSymbol" value="impl_gsm_modem_contacts_get"/> + <arg name="index" type="u" direction="in"> + <tp:docstring> + The index of the contact. + </tp:docstring> + </arg> + <arg name="contact" type="(uss)" direction="out"> + <tp:docstring> + The contact structure containing index, name, and number. + </tp:docstring> + </arg> + </method> + + <method name="List"> + <tp:docstring> + List all contacts on the SIM card. + </tp:docstring> + <annotation name="org.freedesktop.DBus.GLib.Async" value=""/> + <annotation name="org.freedesktop.DBus.GLib.CSymbol" value="impl_gsm_modem_contacts_list"/> + <arg name="results" type="a(uss)" direction="out"> + <tp:docstring> + The list of contacts where each contact has an index, name, and number. + </tp:docstring> + </arg> + </method> + + <method name="Find"> + <tp:docstring> + Find a contact from the SIM card. + </tp:docstring> + <annotation name="org.freedesktop.DBus.GLib.Async" value=""/> + <annotation name="org.freedesktop.DBus.GLib.CSymbol" value="impl_gsm_modem_contacts_find"/> + <arg name="pattern" type="s" direction="in"> + <tp:docstring> + The pattern to search for. + </tp:docstring> + </arg> + <arg name="results" type="a(uss)" direction="out"> + <tp:docstring> + The list of matching contacts where a contact has an index, name, and number. + </tp:docstring> + </arg> + </method> + + <method name="GetCount"> + <tp:docstring> + Get the number of contacts stored on the SIM card. + </tp:docstring> + <annotation name="org.freedesktop.DBus.GLib.Async" value=""/> + <annotation name="org.freedesktop.DBus.GLib.CSymbol" value="impl_gsm_modem_contacts_count"/> + <arg name="count" type="u" direction="out"> + <tp:docstring> + The number of contacts. + </tp:docstring> + </arg> + </method> + + </interface> +</node> diff --git a/introspection/mm-modem-gsm-hso.xml b/introspection/mm-modem-gsm-hso.xml new file mode 100644 index 0000000..d646acc --- /dev/null +++ b/introspection/mm-modem-gsm-hso.xml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="UTF-8" ?> + +<node name="/" xmlns:tp="http://telepathy.freedesktop.org/wiki/DbusSpec#extensions-v0"> + <interface name="org.freedesktop.ModemManager.Modem.Gsm.Hso"> + <method name="Authenticate"> + <tp:docstring> + Authenticate using the passed user name and password. + </tp:docstring> + <annotation name="org.freedesktop.DBus.GLib.Async" value=""/> + <annotation name="org.freedesktop.DBus.GLib.CSymbol" value="impl_hso_authenticate"/> + <arg name="username" type="s" direction="in"> + The user name. + </arg> + <arg name="password" type="s" direction="in"> + The password. + </arg> + </method> + </interface> +</node> diff --git a/introspection/mm-modem-gsm-network.xml b/introspection/mm-modem-gsm-network.xml new file mode 100644 index 0000000..934e8e0 --- /dev/null +++ b/introspection/mm-modem-gsm-network.xml @@ -0,0 +1,202 @@ +<?xml version="1.0" encoding="UTF-8" ?> + +<node name="/" xmlns:tp="http://telepathy.freedesktop.org/wiki/DbusSpec#extensions-v0"> + <interface name="org.freedesktop.ModemManager.Modem.Gsm.Network"> + <method name="Register"> + <tp:docstring> + Register the device to network. + </tp:docstring> + <annotation name="org.freedesktop.DBus.GLib.Async" value=""/> + <annotation name="org.freedesktop.DBus.GLib.CSymbol" value="impl_gsm_modem_register"/> + <arg name="network_id" type="s" direction="in"> + <tp:docstring> + The network ID to register. An empty string can be used to register to the home network. + </tp:docstring> + </arg> + </method> + + <method name="Scan"> + <tp:docstring> + Scan for available networks. + </tp:docstring> + <annotation name="org.freedesktop.DBus.GLib.Async" value=""/> + <annotation name="org.freedesktop.DBus.GLib.CSymbol" value="impl_gsm_modem_scan"/> + <arg name="results" type="aa{ss}" direction="out"> + <tp:docstring> + Found networks. It's an array of dictionaries (strings for keys and values), the list of known keys is the following: status, operator-long, operator-short, operator-num, access-tech. + </tp:docstring> + </arg> + </method> + + <method name="SetApn"> + <tp:docstring> + Set the APN. + </tp:docstring> + <annotation name="org.freedesktop.DBus.GLib.Async" value=""/> + <annotation name="org.freedesktop.DBus.GLib.CSymbol" value="impl_gsm_modem_set_apn"/> + <arg name="apn" type="s" direction="in"> + <tp:docstring> + The APN. + </tp:docstring> + </arg> + </method> + + <method name="GetSignalQuality"> + <tp:docstring> + Get the current signal quality. + </tp:docstring> + <annotation name="org.freedesktop.DBus.GLib.Async" value=""/> + <annotation name="org.freedesktop.DBus.GLib.CSymbol" value="impl_gsm_modem_get_signal_quality"/> + <arg name="quality" type="u" direction="out"> + <tp:docstring> + Signal quality (percent). + </tp:docstring> + </arg> + </method> + + <method name="SetBand"> + <tp:docstring> + Sets the band the device is allowed to use when connecting to a mobile network. + </tp:docstring> + <annotation name="org.freedesktop.DBus.GLib.Async" value=""/> + <annotation name="org.freedesktop.DBus.GLib.CSymbol" value="impl_gsm_modem_set_band"/> + <arg name="band" type="u" direction="in" tp:type="MM_MODEM_GSM_BAND"> + <tp:docstring> + The desired band. Only one band may be specified, and may not be UNKNOWN. + </tp:docstring> + </arg> + </method> + + <method name="GetBand"> + <tp:docstring> + Returns the current band the device is using. (Note for plugin writers: returned value must not be ANY) + </tp:docstring> + <annotation name="org.freedesktop.DBus.GLib.Async" value=""/> + <annotation name="org.freedesktop.DBus.GLib.CSymbol" value="impl_gsm_modem_get_band"/> + <arg name="band" type="u" direction="out" tp:type="MM_MODEM_GSM_BAND"> + <tp:docstring> + The current band. + </tp:docstring> + </arg> + </method> + + <method name="SetNetworkMode"> + <tp:docstring> + Set the desired mode the device may use when connecting to a mobile network. + </tp:docstring> + <annotation name="org.freedesktop.DBus.GLib.Async" value=""/> + <annotation name="org.freedesktop.DBus.GLib.CSymbol" value="impl_gsm_modem_set_network_mode"/> + <arg name="mode" type="u" direction="in" tp:type="MM_MODEM_GSM_MODE"> + <tp:docstring> + The desired network mode. Only one mode may be specified, and may not be UNKNOWN. + </tp:docstring> + </arg> + </method> + + <method name="GetNetworkMode"> + <tp:docstring> + Returns the current network mode of the device. (Note for plugin writers: returned value *may* be ANY) + </tp:docstring> + <annotation name="org.freedesktop.DBus.GLib.Async" value=""/> + <annotation name="org.freedesktop.DBus.GLib.CSymbol" value="impl_gsm_modem_get_network_mode"/> + <arg name="mode" type="u" direction="out" tp:type="MM_MODEM_GSM_MODE"> + <tp:docstring> + Returns the general network mode (ex. 2G/3G preference) of the device. + </tp:docstring> + </arg> + </method> + + <method name="GetRegistrationInfo"> + <tp:docstring> + Get the registration status and the current operator (if registered). + </tp:docstring> + <annotation name="org.freedesktop.DBus.GLib.Async" value=""/> + <annotation name="org.freedesktop.DBus.GLib.CSymbol" value="impl_gsm_modem_get_reg_info"/> + <arg name="info" type="(uss)" direction="out"> + <tp:docstring> + The returned information contains: + * Network status. + * Current operator code. + * Current operator name, + </tp:docstring> + </arg> + </method> + + <signal name="SignalQuality"> + <tp:docstring> + The signal quality changed. + </tp:docstring> + <arg name="quality" type="u"> + <tp:docstring> + The new quality in percent, 0..100. + </tp:docstring> + </arg> + </signal> + + <signal name="RegistrationInfo"> + <tp:docstring> + The registration status changed. + </tp:docstring> + <arg name="status" type="u" tp:type="MM_MODEM_GSM_NETWORK_REG_STATUS"> + <tp:docstring> + The network status. + </tp:docstring> + </arg> + <arg name="operator_code" type="s"> + <tp:docstring> + The current operator code. + </tp:docstring> + </arg> + <arg name="operator_name" type="s"> + <tp:docstring> + The current operator name. + </tp:docstring> + </arg> + </signal> + + <signal name="NetworkMode"> + <tp:docstring> + The network mode changed. + </tp:docstring> + <arg name="mode" type="u" tp:type="MM_MODEM_GSM_MODE"> + <tp:docstring> + The new network mode. + </tp:docstring> + </arg> + </signal> + + <tp:enum name="MM_MODEM_GSM_NETWORK_REG_STATUS" type="u"> + <tp:enumvalue suffix="IDLE" value="0"> + <tp:docstring> + Not registered, not searching for new operator to register. + </tp:docstring> + </tp:enumvalue> + <tp:enumvalue suffix="HOME" value="1"> + <tp:docstring> + Registered on home network. + </tp:docstring> + </tp:enumvalue> + <tp:enumvalue suffix="SEARCHING" value="2"> + <tp:docstring> + Not registered, searching for new operator to register with. + </tp:docstring> + </tp:enumvalue> + <tp:enumvalue suffix="DENIED" value="3"> + <tp:docstring> + Registration denied. + </tp:docstring> + </tp:enumvalue> + <tp:enumvalue suffix="UNKNOWN" value="4"> + <tp:docstring> + Unknown registration status. + </tp:docstring> + </tp:enumvalue> + <tp:enumvalue suffix="ROAMING" value="5"> + <tp:docstring> + Registered on a roaming network. + </tp:docstring> + </tp:enumvalue> + </tp:enum> + + </interface> +</node> diff --git a/introspection/mm-modem-gsm-sms.xml b/introspection/mm-modem-gsm-sms.xml new file mode 100644 index 0000000..081ecc5 --- /dev/null +++ b/introspection/mm-modem-gsm-sms.xml @@ -0,0 +1,156 @@ +<?xml version="1.0" encoding="UTF-8" ?> + +<node name="/" xmlns:tp="http://telepathy.freedesktop.org/wiki/DbusSpec#extensions-v0"> + <interface name="org.freedesktop.ModemManager.Modem.Gsm.SMS"> + <method name="Delete"> + <tp:docstring> + Delete an SMS message. + </tp:docstring> + <annotation name="org.freedesktop.DBus.GLib.Async" value=""/> + <annotation name="org.freedesktop.DBus.GLib.CSymbol" value="impl_gsm_modem_sms_delete"/> + <arg name="index" type="u" direction="in"> + <tp:docstring> + The index of the SMS. + </tp:docstring> + </arg> + </method> + + <method name="Get"> + <tp:docstring> + Retrieve an SMS from the SIM card. + </tp:docstring> + <annotation name="org.freedesktop.DBus.GLib.Async" value=""/> + <annotation name="org.freedesktop.DBus.GLib.CSymbol" value="impl_gsm_modem_sms_get"/> + <arg name="index" type="u" direction="in"> + <tp:docstring> + The index of the SMS. + </tp:docstring> + </arg> + <arg name="sms" type="a{sv}" direction="out"> + <tp:docstring> + A dictionary containing SMS properties of the SMS specified by the given index. This dictionary may contain the following key/value pairs: + + number : string - Phone number (mandatory) + text : string - SMS text (mandatory) + smsc : string - SMS service center number (optional) + validity : uint (0..255) - Specifies when the SMS expires in SMSC (optional) + class : uint (0..3) - Message importance and location (optional) + completed: boolean - Whether all message parts have been received or not (optional) + </tp:docstring> + </arg> + </method> + + <method name="GetFormat"> + <annotation name="org.freedesktop.DBus.GLib.Async" value=""/> + <annotation name="org.freedesktop.DBus.GLib.CSymbol" value="impl_gsm_modem_sms_get_format"/> + <arg name="result" type="u" direction="out"/> + </method> + + <method name="SetFormat"> + <annotation name="org.freedesktop.DBus.GLib.Async" value=""/> + <annotation name="org.freedesktop.DBus.GLib.CSymbol" value="impl_gsm_modem_sms_set_format"/> + <arg name="format" type="u" direction="in"/> + </method> + + <method name="GetSmsc"> + <annotation name="org.freedesktop.DBus.GLib.Async" value=""/> + <annotation name="org.freedesktop.DBus.GLib.CSymbol" value="impl_gsm_modem_sms_get_smsc"/> + <arg name="result" type="s" direction="out"/> + </method> + + <method name="SetSmsc"> + <annotation name="org.freedesktop.DBus.GLib.Async" value=""/> + <annotation name="org.freedesktop.DBus.GLib.CSymbol" value="impl_gsm_modem_sms_set_smsc"/> + <arg name="smsc" type="s" direction="in"/> + </method> + + <method name="List"> + <annotation name="org.freedesktop.DBus.GLib.Async" value=""/> + <annotation name="org.freedesktop.DBus.GLib.CSymbol" value="impl_gsm_modem_sms_list"/> + <arg name="result" type="aa{sv}" direction="out"/> + </method> + + <method name="Save"> + <annotation name="org.freedesktop.DBus.GLib.Async" value=""/> + <annotation name="org.freedesktop.DBus.GLib.CSymbol" value="impl_gsm_modem_sms_save"/> + <arg name="properties" type="a{sv}" direction="in"> + <tp:docstring> + SMS properties to save with the following key values: + + number : string - Phone number (mandatory) + text : string - SMS text (mandatory) + smsc : string - SMS service center number (optional) + validity : uint (0..255) - Specifies when the SMS expires in SMSC (optional) + class : uint (0..3) - Message importance and location (optional) + </tp:docstring> + </arg> + <arg name="index" type="au" direction="out"/> + </method> + + <method name="Send"> + <annotation name="org.freedesktop.DBus.GLib.Async" value=""/> + <annotation name="org.freedesktop.DBus.GLib.CSymbol" value="impl_gsm_modem_sms_send"/> + <arg name="properties" type="a{sv}" direction="in"> + <tp:docstring> + SMS properties to save with the following key values: + + number : string - Phone number (mandatory) + text : string - SMS text (mandatory) + smsc : string - SMS service center number (optional) + validity : uint (0..255) - Specifies when the SMS expires in SMSC (optional) + class : uint (0..3) - Message importance and location (optional) + </tp:docstring> + </arg> + <arg name="result" type="au" direction="out"/> + </method> + + <method name="SendFromStorage"> + <annotation name="org.freedesktop.DBus.GLib.Async" value=""/> + <annotation name="org.freedesktop.DBus.GLib.CSymbol" value="impl_gsm_modem_sms_send_from_storage"/> + <arg name="index" type="u" direction="in"/> + </method> + + <method name="SetIndication"> + <annotation name="org.freedesktop.DBus.GLib.Async" value=""/> + <annotation name="org.freedesktop.DBus.GLib.CSymbol" value="impl_gsm_modem_sms_set_indication"/> + <arg name="mode" type="u" direction="in"/> + <arg name="mt" type="u" direction="in"/> + <arg name="bm" type="u" direction="in"/> + <arg name="ds" type="u" direction="in"/> + <arg name="bfr" type="u" direction="in"/> + </method> + + <signal name="SmsReceived"> + <tp:docstring> + Emitted when any part of a new SMS has been received (but not for subsequent parts, if any). Not all parts may have been received and the message may not be complete; if it is, the 'complete' argument will be TRUE. + </tp:docstring> + <arg name="index" type="u"> + <tp:docstring> + Index of the new SMS. + </tp:docstring> + </arg> + <arg name="complete" type="b"> + <tp:docstring> + TRUE if all message parts have been received, otherwise FALSE. + </tp:docstring> + </arg> + </signal> + + <signal name="Completed"> + <tp:docstring> + Emitted when the complete-ness status of an SMS message changes. An SMS may not necessarily be complete when the first part is received; this signal will be emitted when all parts have been received, even for single-part messages. + </tp:docstring> + <arg name="index" type="u"> + <tp:docstring> + The index of the SMS. + </tp:docstring> + </arg> + <arg name="completed" type="b"> + <tp:docstring> + TRUE if all message parts have been received, otherwise FALSE. + </tp:docstring> + </arg> + </signal> + + </interface> +</node> diff --git a/introspection/mm-modem-gsm.xml b/introspection/mm-modem-gsm.xml new file mode 100644 index 0000000..d6c42c8 --- /dev/null +++ b/introspection/mm-modem-gsm.xml @@ -0,0 +1,86 @@ +<?xml version="1.0" encoding="UTF-8" ?> + +<node name="/" xmlns:tp="http://telepathy.freedesktop.org/wiki/DbusSpec#extensions-v0"> + <interface name="org.freedesktop.ModemManager.Modem.Gsm"> + <tp:flags name="MM_MODEM_GSM_MODE" value-prefix="MM_MODEM_GSM_MODE" type="u"> + <tp:flag suffix="UNKNOWN" value="0x0"> + <tp:docstring>Unknown or invalid mode.</tp:docstring> + </tp:flag> + <tp:flag suffix="ANY" value="0x1"> + <tp:docstring>For certain operations, allow the modem to pick any available mode.</tp:docstring> + </tp:flag> + <tp:flag suffix="GPRS" value="0x2"> + <tp:docstring>GPRS</tp:docstring> + </tp:flag> + <tp:flag suffix="EDGE" value="0x4"> + <tp:docstring>EDGE</tp:docstring> + </tp:flag> + <tp:flag suffix="UMTS" value="0x8"> + <tp:docstring>UMTS (3G)</tp:docstring> + </tp:flag> + <tp:flag suffix="HSDPA" value="0x10"> + <tp:docstring>HSDPA</tp:docstring> + </tp:flag> + <tp:flag suffix="2G_PREFERRED" value="0x20"> + <tp:docstring>Prefer 2G (GPRS or EDGE)</tp:docstring> + </tp:flag> + <tp:flag suffix="3G_PREFERRED" value="0x40"> + <tp:docstring>Prefer 3G (UMTS or HSDPA)</tp:docstring> + </tp:flag> + <tp:flag suffix="2G_ONLY" value="0x80"> + <tp:docstring>Use only 2G (GPRS or EDGE)</tp:docstring> + </tp:flag> + <tp:flag suffix="3G_ONLY" value="0x100"> + <tp:docstring>Use only 3G (UMTS or HSDPA)</tp:docstring> + </tp:flag> + <tp:flag suffix="HSUPA" value="0x200"> + <tp:docstring>HSUPA</tp:docstring> + </tp:flag> + </tp:flags> + + <tp:flags name="MM_MODEM_GSM_BAND" value-prefix="MM_MODEM_GSM_BAND" type="u"> + <tp:docstring> + GSM network and device bands. + </tp:docstring> + <tp:flag suffix="UNKNOWN" value="0x0"> + <tp:docstring>Unknown or invalid band</tp:docstring> + </tp:flag> + <tp:flag suffix="ANY" value="0x1"> + <tp:docstring>For certain operations, allow the modem to select a band automatically.</tp:docstring> + </tp:flag> + <tp:flag suffix="EGSM" value="0x2"> + <tp:docstring>GSM/GPRS/EDGE 900 MHz</tp:docstring> + </tp:flag> + <tp:flag suffix="DCS" value="0x4"> + <tp:docstring>GSM/GPRS/EDGE 1800 MHz</tp:docstring> + </tp:flag> + <tp:flag suffix="PCS" value="0x8"> + <tp:docstring>GSM/GPRS/EDGE 1900 MHz</tp:docstring> + </tp:flag> + <tp:flag suffix="G850" value="0x10"> + <tp:docstring>GSM/GPRS/EDGE 850 MHz</tp:docstring> + </tp:flag> + <tp:flag suffix="U2100" value="0x20"> + <tp:docstring>WCDMA 2100 MHz (Class I)</tp:docstring> + </tp:flag> + <tp:flag suffix="U1800" value="0x40"> + <tp:docstring>WCDMA 3GPP 1800 MHz (Class III)</tp:docstring> + </tp:flag> + <tp:flag suffix="U17IV" value="0x80"> + <tp:docstring>WCDMA 3GPP AWS 1700/2100 MHz (Class IV)</tp:docstring> + </tp:flag> + <tp:flag suffix="U800" value="0x100"> + <tp:docstring>WCDMA 3GPP UMTS 800 MHz (Class VI)</tp:docstring> + </tp:flag> + <tp:flag suffix="U850" value="0x200"> + <tp:docstring>WCDMA 3GPP UMTS 850 MHz (Class V)</tp:docstring> + </tp:flag> + <tp:flag suffix="U900" value="0x400"> + <tp:docstring>WCDMA 3GPP UMTS 900 MHz (Class VIII)</tp:docstring> + </tp:flag> + <tp:flag suffix="U17IX" value="0x800"> + <tp:docstring>WCDMA 3GPP UMTS 1700 MHz (Class IX)</tp:docstring> + </tp:flag> + </tp:flags> + </interface> +</node> diff --git a/introspection/mm-modem-simple.xml b/introspection/mm-modem-simple.xml new file mode 100644 index 0000000..bee1017 --- /dev/null +++ b/introspection/mm-modem-simple.xml @@ -0,0 +1,58 @@ +<?xml version="1.0" encoding="UTF-8" ?> + +<node name="/" xmlns:tp="http://telepathy.freedesktop.org/wiki/DbusSpec#extensions-v0"> + <interface name="org.freedesktop.ModemManager.Modem.Simple"> + + <method name="Connect"> + <tp:docstring> + Do everything needed to connect the modem. + </tp:docstring> + <annotation name="org.freedesktop.DBus.GLib.Async" value=""/> + <annotation name="org.freedesktop.DBus.GLib.CSymbol" value="impl_modem_simple_connect"/> + <arg name="properties" type="a{sv}" direction="in"> + <tp:docstring> + Dictionary of properties needed to get the modem connected. + Each implementation is free to add it's own specific key-value pairs. The predefined + common ones are: + + 'pin' : string + 'network_id' : string + 'band' : uint + 'network_mode' : uint + 'apn' : string + 'number' : string + </tp:docstring> + </arg> + </method> +<!-- + <method name="Disconnect"> + <tp:docstring> + Disconnect modem. + </tp:docstring> + <annotation name="org.freedesktop.DBus.GLib.Async" value=""/> + <annotation name="org.freedesktop.DBus.GLib.CSymbol" value="impl_modem_simple_disconnect"/> + </method> +--> + <method name="GetStatus"> + <tp:docstring> + Get the modem status. + </tp:docstring> + <annotation name="org.freedesktop.DBus.GLib.Async" value=""/> + <annotation name="org.freedesktop.DBus.GLib.CSymbol" value="impl_modem_simple_get_status"/> + <arg name="properties" type="a{sv}" direction="out"> + <tp:docstring> + Dictionary of properties. + Each implementation is free to add it's own specific key-value pairs. The predefined + common ones are: + + 'state' : uint (always) + 'signal_quality' : uint (state >= registered) + 'operator_code' : string (state >= registered) + 'operator_name' : string (state >= registered) + 'band' : uint (state >= registered) + 'network_mode' : uint (state >= registered) + </tp:docstring> + </arg> + </method> + </interface> +</node> diff --git a/introspection/mm-modem.xml b/introspection/mm-modem.xml new file mode 100644 index 0000000..7896fff --- /dev/null +++ b/introspection/mm-modem.xml @@ -0,0 +1,154 @@ +<?xml version="1.0" encoding="UTF-8" ?> + +<node name="/" xmlns:tp="http://telepathy.freedesktop.org/wiki/DbusSpec#extensions-v0"> + + <interface name="org.freedesktop.DBus.Properties"> + <signal name="MmPropertiesChanged"> + <tp:docstring> + One or more properties' values changed. + </tp:docstring> + <arg name="interface" type="s"> + <tp:docstring> + The D-Bus interface of the changed properties. + </tp:docstring> + </arg> + <arg name="properties" type="a{sv}"> + <tp:docstring> + The changed property names and their new values. + </tp:docstring> + </arg> + </signal> + </interface> + + <interface name="org.freedesktop.ModemManager.Modem"> + <method name="Enable"> + <tp:docstring> + Enable the device. Initializes the modem. + </tp:docstring> + <annotation name="org.freedesktop.DBus.GLib.Async" value=""/> + <annotation name="org.freedesktop.DBus.GLib.CSymbol" value="impl_modem_enable"/> + <arg name="enable" type="b" direction="in"> + <tp:docstring> + True to enable the device, False to disable. + </tp:docstring> + </arg> + </method> + + <method name="Connect"> + <tp:docstring> + Dial in. + </tp:docstring> + <annotation name="org.freedesktop.DBus.GLib.Async" value=""/> + <annotation name="org.freedesktop.DBus.GLib.CSymbol" value="impl_modem_connect"/> + <arg name="number" type="s" direction="in"> + <tp:docstring> + The number to use for dialing. + </tp:docstring> + </arg> + </method> + + <method name="Disconnect"> + <tp:docstring> + Disconnect modem. + </tp:docstring> + <annotation name="org.freedesktop.DBus.GLib.Async" value=""/> + <annotation name="org.freedesktop.DBus.GLib.CSymbol" value="impl_modem_disconnect"/> + </method> + + <method name="GetIP4Config"> + <tp:docstring> + Request the IP4 configuration from the device. + Note that it'll only be supported for IPMethod MM_MODEM_IP_METHOD_STATIC. + </tp:docstring> + <annotation name="org.freedesktop.DBus.GLib.Async" value=""/> + <annotation name="org.freedesktop.DBus.GLib.CSymbol" value="impl_modem_get_ip4_config"/> + <arg name="address" type="(uuuu)" direction="out"> + Structure containing IP4 address, DNS1, DNS2, DNS3. + The DNS list is padded with 0's if there's less than 3 DNS servers. + </arg> + </method> + + <method name="GetInfo"> + <tp:docstring> + Get the card information (manufacturer, modem, version). + </tp:docstring> + <annotation name="org.freedesktop.DBus.GLib.Async" value=""/> + <annotation name="org.freedesktop.DBus.GLib.CSymbol" value="impl_modem_get_info"/> + <arg name="info" type="(sss)" direction="out"> + <tp:docstring> + Structure containing manufacturer, model, and version (revision) of the card. + </tp:docstring> + </arg> + </method> + + <property name="Device" type="s" access="read"> + <tp:docstring> + The modem port to use for IP configuration and traffic. + </tp:docstring> + </property> + + <property name="MasterDevice" type="s" access="read"> + <tp:docstring> + The physical modem device reference (ie, USB, PCI, PCMCIA device), which + may be dependent upon the operating system. In Linux for example, this + points to a sysfs path of the usb_device object. + </tp:docstring> + </property> + + <property name="Driver" type="s" access="read"> + <tp:docstring> + The driver handling the device. + </tp:docstring> + </property> + + <property name="Type" type="u" access="read" tp:type="MM_MODEM_TYPE"> + <tp:docstring> + The modem type. + </tp:docstring> + </property> + + <property name="Enabled" type="b" access="read"> + <tp:docstring> + TRUE if the modem is enabled (ie, powered and usable), FALSE if it is disabled. + </tp:docstring> + </property> + + <property name="IpMethod" type="u" access="read" tp:type="MM_MODEM_IP_METHOD"> + <tp:docstring> + The IP configuration method. + </tp:docstring> + </property> + + <tp:enum name="MM_MODEM_TYPE" type="u"> + <tp:enumvalue suffix="GSM" value="1"> + <tp:docstring> + A GSM device. + </tp:docstring> + </tp:enumvalue> + <tp:enumvalue suffix="CDMA" value="2"> + <tp:docstring> + A CDMA device. + </tp:docstring> + </tp:enumvalue> + </tp:enum> + + <tp:enum name="MM_MODEM_IP_METHOD" type="u"> + <tp:enumvalue suffix="PPP" value="0"> + <tp:docstring> + Use PPP to get the address. + </tp:docstring> + </tp:enumvalue> + <tp:enumvalue suffix="STATIC" value="1"> + <tp:docstring> + Static configuration, the modem will provide IP information. + </tp:docstring> + </tp:enumvalue> + <tp:enumvalue suffix="DHCP" value="2"> + <tp:docstring> + Use DHCP + </tp:docstring> + </tp:enumvalue> + </tp:enum> + + </interface> +</node> diff --git a/introspection/mm-serial-error.xml b/introspection/mm-serial-error.xml new file mode 100644 index 0000000..338a223 --- /dev/null +++ b/introspection/mm-serial-error.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" ?> +<tp:errors xmlns:tp="http://telepathy.freedesktop.org/wiki/DbusSpec#extensions-v0" + namespace="org.freedesktop.ModemManager.Modem"> + <tp:copyright>Copyright (C) 2008 Novell, Inc.</tp:copyright> + + <tp:error name="SerialOpenFailed"> + <tp:docstring> + Could not open serial device. + </tp:docstring> + </tp:error> + + <tp:error name="SerialSendFailed"> + <tp:docstring> + Could not write to the serial device. + </tp:docstring> + </tp:error> + + <tp:error name="SerialResponseTimeout"> + <tp:docstring> + A response was not received in time. + </tp:docstring> + </tp:error> + +</tp:errors> diff --git a/marshallers/Makefile.am b/marshallers/Makefile.am new file mode 100644 index 0000000..11ce370 --- /dev/null +++ b/marshallers/Makefile.am @@ -0,0 +1,21 @@ +noinst_LTLIBRARIES = libmarshallers.la + +BUILT_SOURCES = \ + mm-marshal.h \ + mm-marshal.c + +libmarshallers_la_SOURCES = mm-marshal-main.c + +EXTRA_DIST = mm-marshal.list +CLEANFILES = $(BUILT_SOURCES) + +libmarshallers_la_CPPFLAGS = $(MM_CFLAGS) +libmarshallers_la_LIBADD = $(MM_LIBS) + +mm-marshal.h: mm-marshal.list + $(GLIB_GENMARSHAL) $< --prefix=mm_marshal --header > $@ + +mm-marshal.c: mm-marshal.list + $(GLIB_GENMARSHAL) $< --prefix=mm_marshal --body > $@ + +mm-marshal-main.c: mm-marshal.c mm-marshal.h diff --git a/marshallers/mm-marshal-main.c b/marshallers/mm-marshal-main.c new file mode 100644 index 0000000..01e9c81 --- /dev/null +++ b/marshallers/mm-marshal-main.c @@ -0,0 +1,2 @@ +#include "mm-marshal.h" +#include "mm-marshal.c" diff --git a/marshallers/mm-marshal.list b/marshallers/mm-marshal.list new file mode 100644 index 0000000..474d704 --- /dev/null +++ b/marshallers/mm-marshal.list @@ -0,0 +1,8 @@ +VOID:UINT,STRING,STRING +VOID:STRING,STRING,UINT +VOID:OBJECT,UINT +VOID:UINT,BOOLEAN +VOID:UINT,UINT +VOID:UINT,UINT,UINT +VOID:STRING,BOXED + diff --git a/obsolete-patches/NetworkManager-r4359-use-modem-manager.patch b/obsolete-patches/NetworkManager-r4359-use-modem-manager.patch new file mode 100644 index 0000000..ef2a418 --- /dev/null +++ b/obsolete-patches/NetworkManager-r4359-use-modem-manager.patch @@ -0,0 +1,5691 @@ +diff --git a/configure.in b/configure.in +index 8f6f425..921e0bf 100644 +--- a/configure.in ++++ b/configure.in +@@ -488,6 +488,7 @@ src/dhcp-manager/Makefile + src/supplicant-manager/Makefile + src/ppp-manager/Makefile + src/dnsmasq-manager/Makefile ++src/modem-manager/Makefile + src/backends/Makefile + libnm-util/libnm-util.pc + libnm-util/Makefile +diff --git a/introspection/nm-device-cdma.xml b/introspection/nm-device-cdma.xml +index 2b43f8f..22a612b 100644 +--- a/introspection/nm-device-cdma.xml ++++ b/introspection/nm-device-cdma.xml +@@ -2,14 +2,5 @@ + + <node name="/" xmlns:tp="http://telepathy.freedesktop.org/wiki/DbusSpec#extensions-v0"> + <interface name="org.freedesktop.NetworkManager.Device.Cdma"> +- +- <signal name="PropertiesChanged"> +- <arg name="properties" type="a{sv}" tp:type="String_Variant_Map"> +- <tp:docstring> +- A dictionary mapping property names to variant boxed values +- </tp:docstring> +- </arg> +- </signal> +- + </interface> + </node> +diff --git a/introspection/nm-device-gsm.xml b/introspection/nm-device-gsm.xml +index 650d656..0bf7b08 100644 +--- a/introspection/nm-device-gsm.xml ++++ b/introspection/nm-device-gsm.xml +@@ -2,14 +2,5 @@ + + <node name="/" xmlns:tp="http://telepathy.freedesktop.org/wiki/DbusSpec#extensions-v0"> + <interface name="org.freedesktop.NetworkManager.Device.Gsm"> +- +- <signal name="PropertiesChanged"> +- <arg name="properties" type="a{sv}" tp:type="String_Variant_Map"> +- <tp:docstring> +- A dictionary mapping property names to variant boxed values +- </tp:docstring> +- </arg> +- </signal> +- + </interface> + </node> +diff --git a/src/Makefile.am b/src/Makefile.am +index 3c4ce75..3f24eb5 100644 +--- a/src/Makefile.am ++++ b/src/Makefile.am +@@ -5,7 +5,8 @@ SUBDIRS= \ + supplicant-manager \ + ppp-manager \ + backends \ +- dnsmasq-manager ++ dnsmasq-manager \ ++ modem-manager + + INCLUDES = -I${top_srcdir} \ + -I${top_srcdir}/include \ +@@ -15,6 +16,7 @@ INCLUDES = -I${top_srcdir} \ + -I${top_srcdir}/src/dhcp-manager \ + -I${top_srcdir}/src/supplicant-manager \ + -I${top_srcdir}/src/dnsmasq-manager \ ++ -I${top_srcdir}/src/modem-manager \ + -I${top_srcdir}/libnm-util \ + -I${top_srcdir}/callouts + +@@ -59,14 +61,6 @@ NetworkManager_SOURCES = \ + nm-activation-request.h \ + nm-properties-changed-signal.c \ + nm-properties-changed-signal.h \ +- nm-serial-device.c \ +- nm-serial-device.h \ +- nm-gsm-device.c \ +- nm-gsm-device.h \ +- nm-cdma-device.c \ +- nm-cdma-device.h \ +- nm-hso-gsm-device.c \ +- nm-hso-gsm-device.h \ + wpa.c \ + wpa.h \ + nm-netlink.c \ +@@ -89,15 +83,6 @@ nm-device-ethernet-glue.h: $(top_srcdir)/introspection/nm-device-ethernet.xml + nm-device-wifi-glue.h: $(top_srcdir)/introspection/nm-device-wifi.xml + dbus-binding-tool --prefix=nm_device_wifi --mode=glib-server --output=$@ $< + +-nm-serial-device-glue.h: $(top_srcdir)/introspection/nm-device-serial.xml +- dbus-binding-tool --prefix=nm_serial_device --mode=glib-server --output=$@ $< +- +-nm-cdma-device-glue.h: $(top_srcdir)/introspection/nm-device-cdma.xml +- dbus-binding-tool --prefix=nm_cdma_device --mode=glib-server --output=$@ $< +- +-nm-gsm-device-glue.h: $(top_srcdir)/introspection/nm-device-gsm.xml +- dbus-binding-tool --prefix=nm_gsm_device --mode=glib-server --output=$@ $< +- + nm-ip4-config-glue.h: $(top_srcdir)/introspection/nm-ip4-config.xml + dbus-binding-tool --prefix=nm_ip4_config --mode=glib-server --output=$@ $< + +@@ -113,9 +98,6 @@ BUILT_SOURCES = \ + nm-device-interface-glue.h \ + nm-device-ethernet-glue.h \ + nm-device-wifi-glue.h \ +- nm-serial-device-glue.h \ +- nm-cdma-device-glue.h \ +- nm-gsm-device-glue.h \ + nm-ip4-config-glue.h \ + nm-active-connection-glue.h \ + nm-dhcp4-config-glue.h +@@ -150,6 +132,7 @@ NetworkManager_LDADD = \ + ./supplicant-manager/libsupplicant-manager.la \ + ./dnsmasq-manager/libdnsmasq-manager.la \ + ./ppp-manager/libppp-manager.la \ ++ ./modem-manager/libmodem-manager.la \ + ./backends/libnmbackend.la \ + $(top_builddir)/libnm-util/libnm-util.la + +diff --git a/src/NetworkManagerPolicy.c b/src/NetworkManagerPolicy.c +index de85d4c..a97103c 100644 +--- a/src/NetworkManagerPolicy.c ++++ b/src/NetworkManagerPolicy.c +@@ -34,15 +34,13 @@ + #include "nm-device.h" + #include "nm-device-wifi.h" + #include "nm-device-ethernet.h" +-#include "nm-hso-gsm-device.h" +-#include "nm-gsm-device.h" +-#include "nm-cdma-device.h" + #include "nm-dbus-manager.h" + #include "nm-setting-ip4-config.h" + #include "nm-setting-connection.h" + #include "NetworkManagerSystem.h" + #include "nm-named-manager.h" + #include "nm-vpn-manager.h" ++#include "nm-gsm-modem-hso.h" + + typedef struct LookupThread LookupThread; + +@@ -235,7 +233,7 @@ get_best_device (NMManager *manager, NMActRequest **out_req) + } + + /* 'hso' devices never get a gateway from the remote end */ +- if (!can_default && !NM_IS_HSO_GSM_DEVICE (dev)) ++ if (!can_default && !NM_IS_GSM_MODEM_HSO (dev)) + continue; + + prio = nm_device_get_priority (dev); +diff --git a/src/modem-manager/Makefile.am b/src/modem-manager/Makefile.am +new file mode 100644 +index 0000000..5331f65 +--- /dev/null ++++ b/src/modem-manager/Makefile.am +@@ -0,0 +1,45 @@ ++INCLUDES = \ ++ -I${top_srcdir}/src \ ++ -I${top_srcdir}/include \ ++ -I${top_srcdir}/libnm-util \ ++ -I${top_builddir}/marshallers ++ ++noinst_LTLIBRARIES = libmodem-manager.la ++ ++libmodem_manager_la_SOURCES = \ ++ nm-cdma-modem.c \ ++ nm-cdma-modem.h \ ++ nm-gsm-modem.c \ ++ nm-gsm-modem.h \ ++ nm-gsm-modem-hso.c \ ++ nm-gsm-modem-hso.h \ ++ nm-gsm-modem-mbm.c \ ++ nm-gsm-modem-mbm.h \ ++ nm-modem-device.c \ ++ nm-modem-device.h \ ++ nm-modem-manager.h \ ++ nm-modem-manager.c \ ++ nm-modem-types.h ++ ++libmodem_manager_la_CPPFLAGS = \ ++ $(DBUS_CFLAGS) ++ ++libmodem_manager_la_LIBADD = \ ++ $(DBUS_LIBS) \ ++ $(top_builddir)/marshallers/libmarshallers.la ++ ++nm-cdma-device-glue.h: $(top_srcdir)/introspection/nm-device-cdma.xml ++ dbus-binding-tool --prefix=nm_cdma_device --mode=glib-server --output=$@ $< ++ ++nm-gsm-device-glue.h: $(top_srcdir)/introspection/nm-device-gsm.xml ++ dbus-binding-tool --prefix=nm_gsm_device --mode=glib-server --output=$@ $< ++ ++nm-serial-device-glue.h: $(top_srcdir)/introspection/nm-device-serial.xml ++ dbus-binding-tool --prefix=nm_serial_device --mode=glib-server --output=$@ $< ++ ++BUILT_SOURCES = \ ++ nm-cdma-device-glue.h \ ++ nm-gsm-device-glue.h \ ++ nm-serial-device-glue.h ++ ++CLEANFILES = $(BUILT_SOURCES) +diff --git a/src/modem-manager/nm-cdma-modem.c b/src/modem-manager/nm-cdma-modem.c +new file mode 100644 +index 0000000..85532c0 +--- /dev/null ++++ b/src/modem-manager/nm-cdma-modem.c +@@ -0,0 +1,264 @@ ++/* -*- Mode: C; tab-width: 5; indent-tabs-mode: t; c-basic-offset: 5 -*- */ ++ ++#include <string.h> ++ ++#include "nm-cdma-modem.h" ++#include "nm-modem-types.h" ++#include "nm-device-interface.h" ++#include "nm-device-private.h" ++#include "nm-dbus-manager.h" ++#include "nm-setting-connection.h" ++#include "nm-setting-cdma.h" ++#include "nm-utils.h" ++ ++#include "nm-cdma-device-glue.h" ++ ++G_DEFINE_TYPE (NMCdmaModem, nm_cdma_modem, NM_TYPE_MODEM_DEVICE) ++ ++#define NM_CDMA_MODEM_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_CDMA_MODEM, NMCdmaModemPrivate)) ++ ++enum { ++ MODEM_STATE_BEGIN, ++ MODEM_STATE_ENABLE, ++ MODEM_STATE_CONNECT ++}; ++ ++typedef struct { ++ int modem_state; ++} NMCdmaModemPrivate; ++ ++enum { ++ SIGNAL_QUALITY, ++ ++ LAST_SIGNAL ++}; ++ ++static guint signals[LAST_SIGNAL] = { 0 }; ++ ++NMDevice * ++nm_cdma_modem_new (const char *path, ++ const char *data_device, ++ const char *driver) ++{ ++ g_return_val_if_fail (path != NULL, NULL); ++ g_return_val_if_fail (data_device != NULL, NULL); ++ g_return_val_if_fail (driver != NULL, NULL); ++ ++ return (NMDevice *) g_object_new (NM_TYPE_CDMA_MODEM, ++ NM_DEVICE_INTERFACE_UDI, path, ++ NM_DEVICE_INTERFACE_IFACE, data_device, ++ NM_DEVICE_INTERFACE_DRIVER, driver, ++ NM_DEVICE_INTERFACE_MANAGED, TRUE, ++ NM_MODEM_DEVICE_PATH, path, ++ NULL); ++} ++ ++static NMSetting * ++get_setting (NMCdmaModem *device, GType setting_type) ++{ ++ NMActRequest *req; ++ NMSetting *setting = NULL; ++ ++ req = nm_device_get_act_request (NM_DEVICE (device)); ++ if (req) { ++ NMConnection *connection; ++ ++ connection = nm_act_request_get_connection (req); ++ if (connection) ++ setting = nm_connection_get_setting (connection, setting_type); ++ } ++ ++ return setting; ++} ++ ++static void ++state_machine (DBusGProxy *proxy, DBusGProxyCall *call_id, gpointer user_data) ++{ ++ NMCdmaModem *modem = NM_CDMA_MODEM (user_data); ++ NMCdmaModemPrivate *priv = NM_CDMA_MODEM_GET_PRIVATE (modem); ++ NMSettingCdma *setting; ++ GError *error = NULL; ++ ++ setting = NM_SETTING_CDMA (get_setting (modem, NM_TYPE_SETTING_CDMA)); ++ ++ if (call_id) ++ dbus_g_proxy_end_call (proxy, call_id, &error, G_TYPE_INVALID); ++ ++ if (error) { ++ nm_warning ("CDMA modem connection failed: %s", error->message); ++ nm_device_state_changed (NM_DEVICE (modem), NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_NONE); ++ return; ++ } ++ ++ switch (priv->modem_state) { ++ case MODEM_STATE_BEGIN: ++ priv->modem_state = MODEM_STATE_ENABLE; ++ dbus_g_proxy_begin_call (nm_modem_device_get_proxy (NM_MODEM_DEVICE (modem), NULL), ++ "Enable", state_machine, ++ modem, NULL, ++ G_TYPE_BOOLEAN, TRUE, ++ G_TYPE_INVALID); ++ break; ++ case MODEM_STATE_ENABLE: ++ priv->modem_state = MODEM_STATE_CONNECT; ++ dbus_g_proxy_begin_call (nm_modem_device_get_proxy (NM_MODEM_DEVICE (modem), NULL), ++ "Connect", state_machine, ++ modem, NULL, ++ G_TYPE_STRING, nm_setting_cdma_get_number (setting), ++ G_TYPE_INVALID); ++ break; ++ case MODEM_STATE_CONNECT: ++ nm_device_activate_schedule_stage2_device_config (NM_DEVICE (modem)); ++ break; ++ default: ++ nm_warning ("Invalid modem state %d", priv->modem_state); ++ nm_device_state_changed (NM_DEVICE (modem), NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_NONE); ++ break; ++ } ++} ++ ++static NMActStageReturn ++real_act_stage1_prepare (NMDevice *device, NMDeviceStateReason *reason) ++{ ++ NMCdmaModemPrivate *priv = NM_CDMA_MODEM_GET_PRIVATE (device); ++ ++ priv->modem_state = MODEM_STATE_BEGIN; ++ state_machine (NULL, NULL, device); ++ ++ return NM_ACT_STAGE_RETURN_POSTPONE; ++} ++ ++static NMConnection * ++real_get_best_auto_connection (NMDevice *dev, ++ GSList *connections, ++ char **specific_object) ++{ ++ GSList *iter; ++ ++ for (iter = connections; iter; iter = g_slist_next (iter)) { ++ NMConnection *connection = NM_CONNECTION (iter->data); ++ NMSettingConnection *s_con; ++ ++ s_con = (NMSettingConnection *) nm_connection_get_setting (connection, NM_TYPE_SETTING_CONNECTION); ++ g_assert (s_con); ++ ++ if (!nm_setting_connection_get_autoconnect (s_con)) ++ continue; ++ ++ if (strcmp (nm_setting_connection_get_connection_type (s_con), NM_SETTING_CDMA_SETTING_NAME)) ++ continue; ++ ++ return connection; ++ } ++ return NULL; ++} ++ ++static void ++real_connection_secrets_updated (NMDevice *dev, ++ NMConnection *connection, ++ GSList *updated_settings, ++ RequestSecretsCaller caller) ++{ ++ NMActRequest *req; ++ gboolean found = FALSE; ++ GSList *iter; ++ ++ if (caller == SECRETS_CALLER_PPP) { ++ NMPPPManager *ppp_manager; ++ NMSettingCdma *s_cdma = NULL; ++ ++ ppp_manager = nm_modem_device_get_ppp_manager (NM_MODEM_DEVICE (dev)); ++ g_return_if_fail (ppp_manager != NULL); ++ ++ s_cdma = (NMSettingCdma *) nm_connection_get_setting (connection, NM_TYPE_SETTING_CDMA); ++ if (!s_cdma) { ++ /* Shouldn't ever happen */ ++ nm_ppp_manager_update_secrets (ppp_manager, ++ nm_device_get_iface (dev), ++ NULL, ++ NULL, ++ "missing CDMA setting; no secrets could be found."); ++ } else { ++ const char *username = nm_setting_cdma_get_username (s_cdma); ++ const char *password = nm_setting_cdma_get_password (s_cdma); ++ ++ nm_ppp_manager_update_secrets (ppp_manager, ++ nm_device_get_iface (dev), ++ username ? username : "", ++ password ? password : "", ++ NULL); ++ } ++ return; ++ } ++ ++ g_return_if_fail (caller == SECRETS_CALLER_CDMA); ++ g_return_if_fail (nm_device_get_state (dev) == NM_DEVICE_STATE_NEED_AUTH); ++ ++ for (iter = updated_settings; iter; iter = g_slist_next (iter)) { ++ const char *setting_name = (const char *) iter->data; ++ ++ if (!strcmp (setting_name, NM_SETTING_CDMA_SETTING_NAME)) ++ found = TRUE; ++ else ++ nm_warning ("Ignoring updated secrets for setting '%s'.", setting_name); ++ } ++ ++ if (!found) ++ return; ++ ++ req = nm_device_get_act_request (dev); ++ g_assert (req); ++ ++ g_return_if_fail (nm_act_request_get_connection (req) == connection); ++ ++ nm_device_activate_schedule_stage1_device_prepare (dev); ++} ++ ++static const char * ++real_get_ppp_name (NMModemDevice *device, NMConnection *connection) ++{ ++ NMSettingCdma *s_cdma; ++ ++ s_cdma = (NMSettingCdma *) nm_connection_get_setting (connection, NM_TYPE_SETTING_CDMA); ++ g_assert (s_cdma); ++ ++ return nm_setting_cdma_get_username (s_cdma); ++} ++ ++/*****************************************************************************/ ++ ++static void ++nm_cdma_modem_init (NMCdmaModem *self) ++{ ++ nm_device_set_device_type (NM_DEVICE (self), NM_DEVICE_TYPE_CDMA); ++} ++ ++static void ++nm_cdma_modem_class_init (NMCdmaModemClass *klass) ++{ ++ GObjectClass *object_class = G_OBJECT_CLASS (klass); ++ NMDeviceClass *device_class = NM_DEVICE_CLASS (klass); ++ NMModemDeviceClass *modem_class = NM_MODEM_DEVICE_CLASS (klass); ++ ++ g_type_class_add_private (object_class, sizeof (NMCdmaModemPrivate)); ++ ++ /* Virtual methods */ ++ device_class->get_best_auto_connection = real_get_best_auto_connection; ++ device_class->connection_secrets_updated = real_connection_secrets_updated; ++ device_class->act_stage1_prepare = real_act_stage1_prepare; ++ modem_class->get_ppp_name = real_get_ppp_name; ++ ++ /* Signals */ ++ signals[SIGNAL_QUALITY] = ++ g_signal_new ("signal-quality", ++ G_OBJECT_CLASS_TYPE (object_class), ++ G_SIGNAL_RUN_FIRST, ++ G_STRUCT_OFFSET (NMCdmaModemClass, signal_quality), ++ NULL, NULL, ++ g_cclosure_marshal_VOID__UINT, ++ G_TYPE_NONE, 1, ++ G_TYPE_UINT); ++ ++ dbus_g_object_type_install_info (G_TYPE_FROM_CLASS (klass), ++ &dbus_glib_nm_cdma_device_object_info); ++} +diff --git a/src/modem-manager/nm-cdma-modem.h b/src/modem-manager/nm-cdma-modem.h +new file mode 100644 +index 0000000..5dc3c14 +--- /dev/null ++++ b/src/modem-manager/nm-cdma-modem.h +@@ -0,0 +1,36 @@ ++/* -*- Mode: C; tab-width: 5; indent-tabs-mode: t; c-basic-offset: 5 -*- */ ++ ++#ifndef NM_CDMA_MODEM_H ++#define NM_CDMA_MODEM_H ++ ++#include <nm-modem-device.h> ++ ++G_BEGIN_DECLS ++ ++#define NM_TYPE_CDMA_MODEM (nm_cdma_modem_get_type ()) ++#define NM_CDMA_MODEM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_CDMA_MODEM, NMCdmaModem)) ++#define NM_CDMA_MODEM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_CDMA_MODEM, NMCdmaModemClass)) ++#define NM_IS_CDMA_MODEM(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_CDMA_MODEM)) ++#define NM_IS_CDMA_MODEM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_CDMA_MODEM)) ++#define NM_CDMA_MODEM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_CDMA_MODEM, NMCdmaModemClass)) ++ ++typedef struct { ++ NMModemDevice parent; ++} NMCdmaModem; ++ ++typedef struct { ++ NMModemDeviceClass parent; ++ ++ /* Signals */ ++ void (*signal_quality) (NMCdmaModem *modem, guint32 quality); ++} NMCdmaModemClass; ++ ++GType nm_cdma_modem_get_type (void); ++ ++NMDevice *nm_cdma_modem_new (const char *path, ++ const char *data_device, ++ const char *driver); ++ ++G_END_DECLS ++ ++#endif /* NM_CDMA_MODEM_H */ +diff --git a/src/modem-manager/nm-gsm-modem-hso.c b/src/modem-manager/nm-gsm-modem-hso.c +new file mode 100644 +index 0000000..15b79b6 +--- /dev/null ++++ b/src/modem-manager/nm-gsm-modem-hso.c +@@ -0,0 +1,348 @@ ++/* -*- Mode: C; tab-width: 5; indent-tabs-mode: t; c-basic-offset: 5 -*- */ ++ ++#include "nm-gsm-modem-hso.h" ++#include "nm-device-private.h" ++#include "nm-device-interface.h" ++#include "NetworkManagerSystem.h" ++#include "nm-setting-connection.h" ++#include "nm-setting-gsm.h" ++#include "nm-modem-types.h" ++#include "nm-utils.h" ++ ++G_DEFINE_TYPE (NMGsmModemHso, nm_gsm_modem_hso, NM_TYPE_GSM_MODEM) ++ ++#define NM_GSM_MODEM_HSO_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_GSM_MODEM_HSO, NMGsmModemHsoPrivate)) ++ ++typedef struct { ++ char *netdev_iface; ++ NMIP4Config *pending_ip4_config; ++} NMGsmModemHsoPrivate; ++ ++#define HSO_SECRETS_TRIES "gsm-secrets-tries" ++ ++static char * ++get_network_device (NMDevice *device) ++{ ++ char *result = NULL; ++ GError *error = NULL; ++ GValue value = { 0, }; ++ ++ if (!dbus_g_proxy_call (nm_modem_device_get_proxy (NM_MODEM_DEVICE (device), "org.freedesktop.DBus.Properties"), ++ "Get", &error, ++ G_TYPE_STRING, MM_DBUS_INTERFACE_MODEM_GSM_HSO, ++ G_TYPE_STRING, "NetworkDevice", ++ G_TYPE_INVALID, ++ G_TYPE_VALUE, &value, ++ G_TYPE_INVALID)) { ++ nm_warning ("Could not get HSO device's network interface: %s", error->message); ++ g_error_free (error); ++ } else { ++ if (G_VALUE_HOLDS_STRING (&value)) ++ result = g_value_dup_string (&value); ++ else ++ nm_warning ("Could not get HSO device's network interface: wrong type '%s'", ++ G_VALUE_TYPE_NAME (&value)); ++ ++ g_value_unset (&value); ++ } ++ ++ return result; ++} ++ ++NMDevice * ++nm_gsm_modem_hso_new (const char *path, ++ const char *data_device, ++ const char *driver) ++{ ++ NMDevice *device; ++ ++ g_return_val_if_fail (path != NULL, NULL); ++ g_return_val_if_fail (data_device != NULL, NULL); ++ g_return_val_if_fail (driver != NULL, NULL); ++ ++ device = (NMDevice *) g_object_new (NM_TYPE_GSM_MODEM_HSO, ++ NM_DEVICE_INTERFACE_UDI, path, ++ NM_DEVICE_INTERFACE_IFACE, data_device, ++ NM_DEVICE_INTERFACE_DRIVER, driver, ++ NM_DEVICE_INTERFACE_MANAGED, TRUE, ++ NM_MODEM_DEVICE_PATH, path, ++ NULL); ++ ++ if (device) { ++ NMGsmModemHsoPrivate *priv; ++ ++ priv = NM_GSM_MODEM_HSO_GET_PRIVATE (device); ++ priv->netdev_iface = get_network_device (device); ++ if (!priv->netdev_iface) { ++ g_object_unref (device); ++ device = NULL; ++ } ++ } ++ ++ return device; ++} ++ ++/*****************************************************************************/ ++ ++static NMSetting * ++get_setting (NMGsmModemHso *modem, GType setting_type) ++{ ++ NMActRequest *req; ++ NMSetting *setting = NULL; ++ ++ req = nm_device_get_act_request (NM_DEVICE (modem)); ++ if (req) { ++ NMConnection *connection; ++ ++ connection = nm_act_request_get_connection (req); ++ if (connection) ++ setting = nm_connection_get_setting (connection, setting_type); ++ } ++ ++ return setting; ++} ++ ++static void ++hso_auth_done (DBusGProxy *proxy, DBusGProxyCall *call_id, gpointer user_data) ++{ ++ NMDevice *device = NM_DEVICE (user_data); ++ GError *error = NULL; ++ ++ if (dbus_g_proxy_end_call (proxy, call_id, &error, G_TYPE_INVALID)) ++ nm_device_activate_schedule_stage3_ip_config_start (device); ++ else { ++ nm_warning ("Authentication failed: %s", error->message); ++ g_error_free (error); ++ nm_device_state_changed (device, ++ NM_DEVICE_STATE_FAILED, ++ NM_DEVICE_STATE_REASON_MODEM_DIAL_FAILED); ++ } ++} ++ ++static void ++do_hso_auth (NMGsmModemHso *device) ++{ ++ NMSettingGsm *s_gsm; ++ const char *username; ++ const char *password; ++ ++ s_gsm = NM_SETTING_GSM (get_setting (device, NM_TYPE_SETTING_GSM)); ++ username = nm_setting_gsm_get_username (s_gsm); ++ password = nm_setting_gsm_get_password (s_gsm); ++ ++ dbus_g_proxy_begin_call (nm_modem_device_get_proxy (NM_MODEM_DEVICE (device), MM_DBUS_INTERFACE_MODEM_GSM_HSO), ++ "Authenticate", hso_auth_done, ++ device, NULL, ++ G_TYPE_STRING, username ? username : "", ++ G_TYPE_STRING, password ? password : "", ++ G_TYPE_INVALID); ++} ++ ++static NMActStageReturn ++real_act_stage2_config (NMDevice *device, NMDeviceStateReason *reason) ++{ ++ NMActRequest *req; ++ NMConnection *connection; ++ const char *setting_name; ++ GPtrArray *hints = NULL; ++ const char *hint1 = NULL, *hint2 = NULL; ++ guint32 tries; ++ ++ req = nm_device_get_act_request (device); ++ g_assert (req); ++ connection = nm_act_request_get_connection (req); ++ g_assert (connection); ++ ++ setting_name = nm_connection_need_secrets (connection, &hints); ++ if (!setting_name) { ++ do_hso_auth (NM_GSM_MODEM_HSO (device)); ++ return NM_ACT_STAGE_RETURN_POSTPONE; ++ } ++ ++ if (hints) { ++ if (hints->len > 0) ++ hint1 = g_ptr_array_index (hints, 0); ++ if (hints->len > 1) ++ hint2 = g_ptr_array_index (hints, 1); ++ } ++ ++ nm_device_state_changed (device, NM_DEVICE_STATE_NEED_AUTH, NM_DEVICE_STATE_REASON_NONE); ++ ++ tries = GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (connection), HSO_SECRETS_TRIES)); ++ nm_act_request_request_connection_secrets (req, ++ setting_name, ++ tries ? TRUE : FALSE, ++ SECRETS_CALLER_HSO_GSM, ++ hint1, ++ hint2); ++ g_object_set_data (G_OBJECT (connection), HSO_SECRETS_TRIES, GUINT_TO_POINTER (++tries)); ++ ++ if (hints) ++ g_ptr_array_free (hints, TRUE); ++ ++ return NM_ACT_STAGE_RETURN_POSTPONE; ++} ++ ++static void ++get_ip4_config_done (DBusGProxy *proxy, DBusGProxyCall *call_id, gpointer user_data) ++{ ++ NMDevice *device = NM_DEVICE (user_data); ++ guint32 ip4_address; ++ GArray *dns_array; ++ GError *error = NULL; ++ ++ if (dbus_g_proxy_end_call (proxy, call_id, &error, ++ G_TYPE_UINT, &ip4_address, ++ DBUS_TYPE_G_UINT_ARRAY, &dns_array, ++ G_TYPE_INVALID)) { ++ ++ NMGsmModemHsoPrivate *priv = NM_GSM_MODEM_HSO_GET_PRIVATE (device); ++ NMIP4Address *addr; ++ int i; ++ ++ addr = nm_ip4_address_new (); ++ nm_ip4_address_set_address (addr, ip4_address); ++ nm_ip4_address_set_prefix (addr, 32); ++ ++ priv->pending_ip4_config = nm_ip4_config_new (); ++ nm_ip4_config_take_address (priv->pending_ip4_config, addr); ++ ++ for (i = 0; i < dns_array->len; i++) ++ nm_ip4_config_add_nameserver (priv->pending_ip4_config, ++ g_array_index (dns_array, guint32, i)); ++ ++ nm_device_activate_schedule_stage4_ip_config_get (device); ++ } else { ++ nm_warning ("Retrieving IP4 configuration failed: %s", error->message); ++ g_error_free (error); ++ nm_device_state_changed (device, ++ NM_DEVICE_STATE_FAILED, ++ NM_DEVICE_STATE_REASON_IP_CONFIG_UNAVAILABLE); ++ } ++} ++ ++static NMActStageReturn ++real_act_stage3_ip_config_start (NMDevice *device, NMDeviceStateReason *reason) ++{ ++ dbus_g_proxy_begin_call (nm_modem_device_get_proxy (NM_MODEM_DEVICE (device), MM_DBUS_INTERFACE_MODEM_GSM_HSO), ++ "GetIP4Config", get_ip4_config_done, ++ device, NULL, ++ G_TYPE_INVALID); ++ ++ return NM_ACT_STAGE_RETURN_POSTPONE; ++} ++ ++static NMActStageReturn ++real_act_stage4_get_ip4_config (NMDevice *device, ++ NMIP4Config **config, ++ NMDeviceStateReason *reason) ++{ ++ NMGsmModemHso *self = NM_GSM_MODEM_HSO (device); ++ NMGsmModemHsoPrivate *priv = NM_GSM_MODEM_HSO_GET_PRIVATE (self); ++ gboolean no_firmware = FALSE; ++ ++ nm_device_set_ip_iface (device, priv->netdev_iface); ++ if (!nm_device_hw_bring_up (device, TRUE, &no_firmware)) { ++ if (no_firmware) ++ *reason = NM_DEVICE_STATE_REASON_FIRMWARE_MISSING; ++ else ++ *reason = NM_DEVICE_STATE_REASON_CONFIG_FAILED; ++ return NM_ACT_STAGE_RETURN_FAILURE; ++ } ++ ++ *config = priv->pending_ip4_config; ++ priv->pending_ip4_config = NULL; ++ ++ return NM_ACT_STAGE_RETURN_SUCCESS; ++} ++ ++static void ++real_deactivate (NMDevice *device) ++{ ++ NMGsmModemHsoPrivate *priv = NM_GSM_MODEM_HSO_GET_PRIVATE (device); ++ ++ if (priv->pending_ip4_config) { ++ g_object_unref (priv->pending_ip4_config); ++ priv->pending_ip4_config = NULL; ++ } ++ ++ if (priv->netdev_iface) { ++ nm_system_device_flush_ip4_routes_with_iface (priv->netdev_iface); ++ nm_system_device_flush_ip4_addresses_with_iface (priv->netdev_iface); ++ nm_system_device_set_up_down_with_iface (priv->netdev_iface, FALSE, NULL); ++ } ++ nm_device_set_ip_iface (device, NULL); ++ ++ if (NM_DEVICE_CLASS (nm_gsm_modem_hso_parent_class)->deactivate) ++ NM_DEVICE_CLASS (nm_gsm_modem_hso_parent_class)->deactivate (device); ++} ++ ++static gboolean ++real_hw_is_up (NMDevice *device) ++{ ++ NMGsmModemHsoPrivate *priv = NM_GSM_MODEM_HSO_GET_PRIVATE (device); ++ NMDeviceState state; ++ ++ state = nm_device_interface_get_state (NM_DEVICE_INTERFACE (device)); ++ if (priv->pending_ip4_config || state == NM_DEVICE_STATE_IP_CONFIG || state == NM_DEVICE_STATE_ACTIVATED) ++ return nm_system_device_is_up_with_iface (priv->netdev_iface); ++ ++ return TRUE; ++} ++ ++static gboolean ++real_hw_bring_up (NMDevice *device, gboolean *no_firmware) ++{ ++ NMGsmModemHsoPrivate *priv = NM_GSM_MODEM_HSO_GET_PRIVATE (device); ++ NMDeviceState state; ++ ++ state = nm_device_interface_get_state (NM_DEVICE_INTERFACE (device)); ++ if (priv->pending_ip4_config || state == NM_DEVICE_STATE_IP_CONFIG || state == NM_DEVICE_STATE_ACTIVATED) ++ return nm_system_device_set_up_down_with_iface (priv->netdev_iface, TRUE, no_firmware); ++ ++ return TRUE; ++} ++ ++static void ++real_connect (NMModemDevice *modem, const char *number) ++{ ++ nm_device_activate_schedule_stage2_device_config (NM_DEVICE (modem)); ++} ++ ++/*****************************************************************************/ ++ ++static void ++nm_gsm_modem_hso_init (NMGsmModemHso *self) ++{ ++} ++ ++static void ++finalize (GObject *object) ++{ ++ NMGsmModemHsoPrivate *priv = NM_GSM_MODEM_HSO_GET_PRIVATE (object); ++ ++ g_free (priv->netdev_iface); ++ ++ G_OBJECT_CLASS (nm_gsm_modem_hso_parent_class)->finalize (object); ++} ++ ++static void ++nm_gsm_modem_hso_class_init (NMGsmModemHsoClass *klass) ++{ ++ GObjectClass *object_class = G_OBJECT_CLASS (klass); ++ NMDeviceClass *device_class = NM_DEVICE_CLASS (klass); ++ NMModemDeviceClass *modem_class = NM_MODEM_DEVICE_CLASS (klass); ++ ++ g_type_class_add_private (object_class, sizeof (NMGsmModemHsoPrivate)); ++ ++ object_class->finalize = finalize; ++ ++ device_class->act_stage2_config = real_act_stage2_config; ++ device_class->act_stage3_ip_config_start = real_act_stage3_ip_config_start; ++ device_class->act_stage4_get_ip4_config = real_act_stage4_get_ip4_config; ++ device_class->deactivate = real_deactivate; ++ device_class->hw_is_up = real_hw_is_up; ++ device_class->hw_bring_up = real_hw_bring_up; ++ ++ modem_class->connect = real_connect; ++} +diff --git a/src/modem-manager/nm-gsm-modem-hso.h b/src/modem-manager/nm-gsm-modem-hso.h +new file mode 100644 +index 0000000..9b16b0b +--- /dev/null ++++ b/src/modem-manager/nm-gsm-modem-hso.h +@@ -0,0 +1,33 @@ ++/* -*- Mode: C; tab-width: 5; indent-tabs-mode: t; c-basic-offset: 5 -*- */ ++ ++#ifndef NM_GSM_MODEM_HSO_H ++#define NM_GSM_MODEM_HSO_H ++ ++#include <nm-gsm-modem.h> ++ ++G_BEGIN_DECLS ++ ++#define NM_TYPE_GSM_MODEM_HSO (nm_gsm_modem_hso_get_type ()) ++#define NM_GSM_MODEM_HSO(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_GSM_MODEM_HSO, NMGsmModemHso)) ++#define NM_GSM_MODEM_HSO_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_GSM_MODEM_HSO, NMGsmModemHsoClass)) ++#define NM_IS_GSM_MODEM_HSO(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_GSM_MODEM_HSO)) ++#define NM_IS_GSM_MODEM_HSO_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_GSM_MODEM_HSO)) ++#define NM_GSM_MODEM_HSO_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_GSM_MODEM_HSO, NMGsmModemHsoClass)) ++ ++typedef struct { ++ NMGsmModem parent; ++} NMGsmModemHso; ++ ++typedef struct { ++ NMGsmModemClass parent; ++} NMGsmModemHsoClass; ++ ++GType nm_gsm_modem_hso_get_type (void); ++ ++NMDevice *nm_gsm_modem_hso_new (const char *path, ++ const char *data_device, ++ const char *driver); ++ ++G_END_DECLS ++ ++#endif /* NM_GSM_MODEM_HSO_H */ +diff --git a/src/modem-manager/nm-gsm-modem-mbm.c b/src/modem-manager/nm-gsm-modem-mbm.c +new file mode 100644 +index 0000000..37ca844 +--- /dev/null ++++ b/src/modem-manager/nm-gsm-modem-mbm.c +@@ -0,0 +1,261 @@ ++/* -*- Mode: C; tab-width: 5; indent-tabs-mode: t; c-basic-offset: 5 -*- */ ++/* ++ Additions to NetworkManager, network-manager-applet and modemmanager ++ for supporting Ericsson modules like F3507g. ++ ++ Author: Per Hallsmark <per@hallsmark.se> ++ ++ 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. ++ ++ 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 "nm-gsm-modem-mbm.h" ++#include "nm-device-private.h" ++#include "nm-device-interface.h" ++#include "NetworkManagerSystem.h" ++#include "nm-setting-connection.h" ++#include "nm-setting-gsm.h" ++#include "nm-modem-types.h" ++#include "nm-utils.h" ++ ++G_DEFINE_TYPE (NMGsmModemMbm, nm_gsm_modem_mbm, NM_TYPE_GSM_MODEM) ++ ++#define NM_GSM_MODEM_MBM_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_GSM_MODEM_MBM, NMGsmModemMbmPrivate)) ++ ++typedef struct { ++ char *netdev_iface; ++ NMIP4Config *pending_ip4_config; ++} NMGsmModemMbmPrivate; ++ ++#define MBM_SECRETS_TRIES "gsm-secrets-tries" ++ ++static char * ++get_network_device (NMDevice *device) ++{ ++ char *result = NULL; ++ GError *error = NULL; ++ GValue value = { 0, }; ++ ++ if (!dbus_g_proxy_call (nm_modem_device_get_proxy (NM_MODEM_DEVICE (device), "org.freedesktop.DBus.Properties"), ++ "Get", &error, ++ G_TYPE_STRING, MM_DBUS_INTERFACE_MODEM_GSM_MBM, ++ G_TYPE_STRING, "NetworkDevice", ++ G_TYPE_INVALID, ++ G_TYPE_VALUE, &value, ++ G_TYPE_INVALID)) { ++ nm_warning ("Could not get MBM device's network interface: %s", error->message); ++ g_error_free (error); ++ } else { ++ if (G_VALUE_HOLDS_STRING (&value)) ++ result = g_value_dup_string (&value); ++ else ++ nm_warning ("Could not get MBM device's network interface: wrong type '%s'", ++ G_VALUE_TYPE_NAME (&value)); ++ ++ g_value_unset (&value); ++ } ++ ++ return result; ++} ++ ++NMDevice * ++nm_gsm_modem_mbm_new (const char *path, ++ const char *data_device, ++ const char *driver) ++{ ++ NMDevice *device; ++ ++ g_return_val_if_fail (path != NULL, NULL); ++ g_return_val_if_fail (data_device != NULL, NULL); ++ g_return_val_if_fail (driver != NULL, NULL); ++ ++ device = (NMDevice *) g_object_new (NM_TYPE_GSM_MODEM_MBM, ++ NM_DEVICE_INTERFACE_UDI, path, ++ NM_DEVICE_INTERFACE_IFACE, data_device, ++ NM_DEVICE_INTERFACE_DRIVER, driver, ++ NM_DEVICE_INTERFACE_MANAGED, TRUE, ++ NM_MODEM_DEVICE_PATH, path, ++ NULL); ++ ++ if (device) { ++ NMGsmModemMbmPrivate *priv; ++ ++ priv = NM_GSM_MODEM_MBM_GET_PRIVATE (device); ++ priv->netdev_iface = get_network_device (device); ++ if (!priv->netdev_iface) { ++ g_object_unref (device); ++ device = NULL; ++ } ++ } ++ ++ return device; ++} ++ ++/*****************************************************************************/ ++ ++#if 0 ++static NMSetting * ++get_setting (NMGsmModemMbm *modem, GType setting_type) ++{ ++ NMActRequest *req; ++ NMSetting *setting = NULL; ++ ++ req = nm_device_get_act_request (NM_DEVICE (modem)); ++ if (req) { ++ NMConnection *connection; ++ ++ connection = nm_act_request_get_connection (req); ++ if (connection) ++ setting = nm_connection_get_setting (connection, setting_type); ++ } ++ ++ return setting; ++} ++#endif ++ ++#if 0 ++static NMActStageReturn ++real_act_stage2_config (NMDevice *device, NMDeviceStateReason *reason) ++{ ++ NMActRequest *req; ++ NMConnection *connection; ++ const char *setting_name; ++ GPtrArray *hints = NULL; ++ const char *hint1 = NULL, *hint2 = NULL; ++ guint32 tries; ++ ++ req = nm_device_get_act_request (device); ++ g_assert (req); ++ connection = nm_act_request_get_connection (req); ++ g_assert (connection); ++ ++ setting_name = nm_connection_need_secrets (connection, &hints); ++ if (!setting_name) { ++ // do_mbm_auth (NM_GSM_MODEM_MBM (device)); ++ return NM_ACT_STAGE_RETURN_POSTPONE; ++ } ++ ++ if (hints) { ++ if (hints->len > 0) ++ hint1 = g_ptr_array_index (hints, 0); ++ if (hints->len > 1) ++ hint2 = g_ptr_array_index (hints, 1); ++ } ++ ++ nm_device_state_changed (device, NM_DEVICE_STATE_NEED_AUTH, NM_DEVICE_STATE_REASON_NONE); ++ ++ tries = GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (connection), MBM_SECRETS_TRIES)); ++ nm_act_request_request_connection_secrets (req, ++ setting_name, ++ tries ? TRUE : FALSE, ++ SECRETS_CALLER_MBM_GSM, ++ hint1, ++ hint2); ++ g_object_set_data (G_OBJECT (connection), MBM_SECRETS_TRIES, GUINT_TO_POINTER (++tries)); ++ ++ if (hints) ++ g_ptr_array_free (hints, TRUE); ++ ++ return NM_ACT_STAGE_RETURN_POSTPONE; ++} ++#endif ++ ++static void ++real_deactivate (NMDevice *device) ++{ ++ NMGsmModemMbmPrivate *priv = NM_GSM_MODEM_MBM_GET_PRIVATE (device); ++ ++ if (priv->pending_ip4_config) { ++ g_object_unref (priv->pending_ip4_config); ++ priv->pending_ip4_config = NULL; ++ } ++ ++ if (priv->netdev_iface) { ++ nm_system_device_flush_ip4_routes_with_iface (priv->netdev_iface); ++ nm_system_device_flush_ip4_addresses_with_iface (priv->netdev_iface); ++ nm_system_device_set_up_down_with_iface (priv->netdev_iface, FALSE, NULL); ++ } ++ nm_device_set_ip_iface (device, NULL); ++ ++ if (NM_DEVICE_CLASS (nm_gsm_modem_mbm_parent_class)->deactivate) ++ NM_DEVICE_CLASS (nm_gsm_modem_mbm_parent_class)->deactivate (device); ++} ++ ++static gboolean ++real_hw_is_up (NMDevice *device) ++{ ++ NMGsmModemMbmPrivate *priv = NM_GSM_MODEM_MBM_GET_PRIVATE (device); ++ ++ if (priv->netdev_iface) ++ return nm_system_device_is_up_with_iface (priv->netdev_iface); ++ ++ return TRUE; ++} ++ ++static gboolean ++real_hw_bring_up (NMDevice *device, gboolean *no_firmware) ++{ ++ NMGsmModemMbmPrivate *priv = NM_GSM_MODEM_MBM_GET_PRIVATE (device); ++ ++ if (priv->netdev_iface) ++ return nm_system_device_set_up_down_with_iface (priv->netdev_iface, TRUE, no_firmware); ++ ++ return TRUE; ++} ++ ++static void ++real_connect (NMModemDevice *modem, const char *number) ++{ ++ nm_device_activate_schedule_stage2_device_config (NM_DEVICE (modem)); ++} ++ ++/*****************************************************************************/ ++ ++static void ++nm_gsm_modem_mbm_init (NMGsmModemMbm *self) ++{ ++} ++ ++static void ++finalize (GObject *object) ++{ ++ NMGsmModemMbmPrivate *priv = NM_GSM_MODEM_MBM_GET_PRIVATE (object); ++ ++ g_free (priv->netdev_iface); ++ ++ G_OBJECT_CLASS (nm_gsm_modem_mbm_parent_class)->finalize (object); ++} ++ ++static void ++nm_gsm_modem_mbm_class_init (NMGsmModemMbmClass *klass) ++{ ++ GObjectClass *object_class = G_OBJECT_CLASS (klass); ++ NMDeviceClass *device_class = NM_DEVICE_CLASS (klass); ++ NMModemDeviceClass *modem_class = NM_MODEM_DEVICE_CLASS (klass); ++ ++ g_type_class_add_private (object_class, sizeof (NMGsmModemMbmPrivate)); ++ ++ object_class->finalize = finalize; ++ ++#if 0 ++ device_class->act_stage2_config = real_act_stage2_config; ++#endif ++ device_class->deactivate = real_deactivate; ++ device_class->hw_is_up = real_hw_is_up; ++ device_class->hw_bring_up = real_hw_bring_up; ++ ++ modem_class->connect = real_connect; ++} +diff --git a/src/modem-manager/nm-gsm-modem-mbm.h b/src/modem-manager/nm-gsm-modem-mbm.h +new file mode 100644 +index 0000000..1f49fda +--- /dev/null ++++ b/src/modem-manager/nm-gsm-modem-mbm.h +@@ -0,0 +1,54 @@ ++/* -*- Mode: C; tab-width: 5; indent-tabs-mode: t; c-basic-offset: 5 -*- */ ++/* ++ Additions to NetworkManager, network-manager-applet and modemmanager ++ for supporting Ericsson modules like F3507g. ++ ++ Author: Per Hallsmark <per@hallsmark.se> ++ ++ 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. ++ ++ 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 NM_GSM_MODEM_MBM_H ++#define NM_GSM_MODEM_MBM_H ++ ++#include <nm-gsm-modem.h> ++ ++G_BEGIN_DECLS ++ ++#define NM_TYPE_GSM_MODEM_MBM (nm_gsm_modem_mbm_get_type ()) ++#define NM_GSM_MODEM_MBM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_GSM_MODEM_MBM, NMGsmModemMbm)) ++#define NM_GSM_MODEM_MBM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_GSM_MODEM_MBM, NMGsmModemMbmClass)) ++#define NM_IS_GSM_MODEM_MBM(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_GSM_MODEM_MBM)) ++#define NM_IS_GSM_MODEM_MBM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_GSM_MODEM_MBM)) ++#define NM_GSM_MODEM_MBM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_GSM_MODEM_MBM, NMGsmModemMbmClass)) ++ ++typedef struct { ++ NMGsmModem parent; ++} NMGsmModemMbm; ++ ++typedef struct { ++ NMGsmModemClass parent; ++} NMGsmModemMbmClass; ++ ++GType nm_gsm_modem_mbm_get_type (void); ++ ++NMDevice *nm_gsm_modem_mbm_new (const char *path, const char *data_device, ++ const char *driver); ++ ++G_END_DECLS ++ ++#endif /* NM_GSM_MODEM_MBM_H */ +diff --git a/src/modem-manager/nm-gsm-modem.c b/src/modem-manager/nm-gsm-modem.c +new file mode 100644 +index 0000000..047e76c +--- /dev/null ++++ b/src/modem-manager/nm-gsm-modem.c +@@ -0,0 +1,354 @@ ++/* -*- Mode: C; tab-width: 5; indent-tabs-mode: t; c-basic-offset: 5 -*- */ ++ ++#include <string.h> ++#include "nm-gsm-modem.h" ++#include "nm-device-private.h" ++#include "nm-device-interface.h" ++#include "nm-setting-connection.h" ++#include "nm-setting-gsm.h" ++#include "nm-modem-types.h" ++#include "nm-utils.h" ++ ++#include "nm-gsm-device-glue.h" ++ ++G_DEFINE_TYPE (NMGsmModem, nm_gsm_modem, NM_TYPE_MODEM_DEVICE) ++ ++#define NM_GSM_MODEM_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_GSM_MODEM, NMGsmModemPrivate)) ++ ++enum { ++ MODEM_STATE_BEGIN, ++ MODEM_STATE_ENABLE, ++ MODEM_STATE_SET_PIN, ++ MODEM_STATE_SET_APN, ++ MODEM_STATE_SET_BAND, ++ MODEM_STATE_SET_NETWORK_MODE, ++ MODEM_STATE_REGISTER, ++ MODEM_STATE_FAILED, ++}; ++ ++typedef struct { ++ int modem_state; ++} NMGsmModemPrivate; ++ ++NMDevice * ++nm_gsm_modem_new (const char *path, ++ const char *data_device, ++ const char *driver) ++{ ++ g_return_val_if_fail (path != NULL, NULL); ++ g_return_val_if_fail (data_device != NULL, NULL); ++ g_return_val_if_fail (driver != NULL, NULL); ++ ++ return (NMDevice *) g_object_new (NM_TYPE_GSM_MODEM, ++ NM_DEVICE_INTERFACE_UDI, path, ++ NM_DEVICE_INTERFACE_IFACE, data_device, ++ NM_DEVICE_INTERFACE_DRIVER, driver, ++ NM_DEVICE_INTERFACE_MANAGED, TRUE, ++ NM_MODEM_DEVICE_PATH, path, ++ NULL); ++} ++ ++static NMSetting * ++get_setting (NMGsmModem *modem, GType setting_type) ++{ ++ NMActRequest *req; ++ NMSetting *setting = NULL; ++ ++ req = nm_device_get_act_request (NM_DEVICE (modem)); ++ if (req) { ++ NMConnection *connection; ++ ++ connection = nm_act_request_get_connection (req); ++ if (connection) ++ setting = nm_connection_get_setting (connection, setting_type); ++ } ++ ++ return setting; ++} ++ ++#define get_proxy(dev,iface) (nm_modem_device_get_proxy(NM_MODEM_DEVICE (dev), iface)) ++ ++static void ++state_machine (DBusGProxy *proxy, DBusGProxyCall *call_id, gpointer user_data) ++{ ++ NMGsmModem *modem = NM_GSM_MODEM (user_data); ++ NMGsmModemPrivate *priv = NM_GSM_MODEM_GET_PRIVATE (modem); ++ NMSettingGsm *setting; ++ const char *secret = NULL; ++ const char *secret_name = NULL; ++ const char *str; ++ GError *error = NULL; ++ int i; ++ gboolean retry_secret = FALSE; ++ ++ setting = NM_SETTING_GSM (get_setting (modem, NM_TYPE_SETTING_GSM)); ++ ++ if (call_id) ++ dbus_g_proxy_end_call (proxy, call_id, &error, G_TYPE_INVALID); ++ ++ if (error) { ++ g_debug ("%s", dbus_g_error_get_name (error)); ++ ++ if (dbus_g_error_has_name (error, MM_MODEM_ERROR_SIM_PIN)) { ++ secret = nm_setting_gsm_get_pin (setting); ++ secret_name = NM_SETTING_GSM_PIN; ++ priv->modem_state = MODEM_STATE_SET_PIN; ++ } else if (dbus_g_error_has_name (error, MM_MODEM_ERROR_SIM_PUK)) { ++ secret = nm_setting_gsm_get_puk (setting); ++ secret_name = NM_SETTING_GSM_PUK; ++ priv->modem_state = MODEM_STATE_SET_PIN; ++ } else if (dbus_g_error_has_name (error, MM_MODEM_ERROR_SIM_WRONG)) { ++ g_object_set (setting, NM_SETTING_GSM_PIN, NULL, NULL); ++ secret_name = NM_SETTING_GSM_PIN; ++ retry_secret = TRUE; ++ priv->modem_state = MODEM_STATE_SET_PIN; ++ } ++ ++ /* FIXME: Hacks to ignore failures of setting band and network mode for now ++ since only Huawei module supports it. Remove when ModemManager rules. ++ */ ++ else if (dbus_g_error_has_name (error, MM_MODEM_ERROR_OPERATION_NOT_SUPPORTED) && ++ (priv->modem_state == MODEM_STATE_SET_BAND || ++ priv->modem_state == MODEM_STATE_SET_NETWORK_MODE)) { ++ ++ nm_warning ("Modem does not support setting %s, ignoring", ++ priv->modem_state == MODEM_STATE_SET_BAND ? "band" : "network mode"); ++ } else { ++ priv->modem_state = MODEM_STATE_FAILED; ++ nm_warning ("GSM modem connection failed: %s", error->message); ++ } ++ ++ g_error_free (error); ++ } ++ ++ again: ++ ++ switch (priv->modem_state) { ++ case MODEM_STATE_BEGIN: ++ priv->modem_state = MODEM_STATE_ENABLE; ++ dbus_g_proxy_begin_call (get_proxy (modem, MM_DBUS_INTERFACE_MODEM), ++ "Enable", state_machine, ++ modem, NULL, ++ G_TYPE_BOOLEAN, TRUE, ++ G_TYPE_INVALID); ++ break; ++ ++ case MODEM_STATE_SET_PIN: ++ if (secret) { ++ priv->modem_state = MODEM_STATE_ENABLE; ++ dbus_g_proxy_begin_call (get_proxy (modem, MM_DBUS_INTERFACE_MODEM_GSM_CARD), ++ "SendPin", state_machine, ++ modem, NULL, ++ G_TYPE_STRING, secret, ++ G_TYPE_INVALID); ++ } else { ++ nm_device_state_changed (NM_DEVICE (modem), NM_DEVICE_STATE_NEED_AUTH, NM_DEVICE_STATE_REASON_NONE); ++ nm_act_request_request_connection_secrets (nm_device_get_act_request (NM_DEVICE (modem)), ++ NM_SETTING_GSM_SETTING_NAME, ++ retry_secret, ++ SECRETS_CALLER_GSM, ++ secret_name, ++ NULL); ++ ++ } ++ break; ++ ++ case MODEM_STATE_ENABLE: ++ priv->modem_state = MODEM_STATE_SET_APN; ++ str = nm_setting_gsm_get_apn (setting); ++ ++ if (str) ++ dbus_g_proxy_begin_call (get_proxy (modem, MM_DBUS_INTERFACE_MODEM_GSM_NETWORK), ++ "SetApn", state_machine, ++ modem, NULL, ++ G_TYPE_STRING, str, ++ G_TYPE_INVALID); ++ else ++ goto again; ++ ++ break; ++ case MODEM_STATE_SET_APN: ++ priv->modem_state = MODEM_STATE_SET_BAND; ++ i = nm_setting_gsm_get_band (setting); ++ ++ if (i) ++ dbus_g_proxy_begin_call (get_proxy (modem, MM_DBUS_INTERFACE_MODEM_GSM_NETWORK), ++ "SetBand", state_machine, ++ modem, NULL, ++ G_TYPE_UINT, (guint32) i, ++ G_TYPE_INVALID); ++ else ++ goto again; ++ ++ break; ++ ++ case MODEM_STATE_SET_BAND: ++ priv->modem_state = MODEM_STATE_SET_NETWORK_MODE; ++ i = nm_setting_gsm_get_network_type (setting); ++ ++ if (i) ++ dbus_g_proxy_begin_call (get_proxy (modem, MM_DBUS_INTERFACE_MODEM_GSM_NETWORK), ++ "SetNetworkMode", state_machine, ++ modem, NULL, ++ G_TYPE_UINT, (guint32) i, ++ G_TYPE_INVALID); ++ else ++ goto again; ++ ++ break; ++ ++ case MODEM_STATE_SET_NETWORK_MODE: ++ priv->modem_state = MODEM_STATE_REGISTER; ++ ++ str = nm_setting_gsm_get_network_id (setting); ++ dbus_g_proxy_begin_call_with_timeout (get_proxy (modem, MM_DBUS_INTERFACE_MODEM_GSM_NETWORK), ++ "Register", state_machine, ++ modem, NULL, 120000, ++ G_TYPE_STRING, str ? str : "", ++ G_TYPE_INVALID); ++ break; ++ ++ case MODEM_STATE_REGISTER: ++ nm_modem_device_connect (NM_MODEM_DEVICE (modem), nm_setting_gsm_get_number (setting)); ++ break; ++ case MODEM_STATE_FAILED: ++ default: ++ nm_device_state_changed (NM_DEVICE (modem), NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_NONE); ++ break; ++ } ++} ++ ++static NMActStageReturn ++real_act_stage1_prepare (NMDevice *device, NMDeviceStateReason *reason) ++{ ++ NMGsmModemPrivate *priv = NM_GSM_MODEM_GET_PRIVATE (device); ++ ++ priv->modem_state = MODEM_STATE_BEGIN; ++ state_machine (NULL, NULL, device); ++ ++ return NM_ACT_STAGE_RETURN_POSTPONE; ++} ++ ++static NMConnection * ++real_get_best_auto_connection (NMDevice *dev, ++ GSList *connections, ++ char **specific_object) ++{ ++ GSList *iter; ++ ++ for (iter = connections; iter; iter = g_slist_next (iter)) { ++ NMConnection *connection = NM_CONNECTION (iter->data); ++ NMSettingConnection *s_con; ++ ++ s_con = (NMSettingConnection *) nm_connection_get_setting (connection, NM_TYPE_SETTING_CONNECTION); ++ g_assert (s_con); ++ ++ if (!nm_setting_connection_get_autoconnect (s_con)) ++ continue; ++ ++ if (strcmp (nm_setting_connection_get_connection_type (s_con), NM_SETTING_GSM_SETTING_NAME)) ++ continue; ++ ++ return connection; ++ } ++ return NULL; ++} ++ ++static void ++real_connection_secrets_updated (NMDevice *dev, ++ NMConnection *connection, ++ GSList *updated_settings, ++ RequestSecretsCaller caller) ++{ ++ NMActRequest *req; ++ gboolean found = FALSE; ++ GSList *iter; ++ ++ if (caller == SECRETS_CALLER_PPP) { ++ NMPPPManager *ppp_manager; ++ NMSettingGsm *s_gsm = NULL; ++ ++ ppp_manager = nm_modem_device_get_ppp_manager (NM_MODEM_DEVICE (dev)); ++ g_return_if_fail (ppp_manager != NULL); ++ ++ s_gsm = (NMSettingGsm *) nm_connection_get_setting (connection, NM_TYPE_SETTING_GSM); ++ if (!s_gsm) { ++ /* Shouldn't ever happen */ ++ nm_ppp_manager_update_secrets (ppp_manager, ++ nm_device_get_iface (dev), ++ NULL, ++ NULL, ++ "missing GSM setting; no secrets could be found."); ++ } else { ++ const char *username = nm_setting_gsm_get_username (s_gsm); ++ const char *password = nm_setting_gsm_get_password (s_gsm); ++ ++ nm_ppp_manager_update_secrets (ppp_manager, ++ nm_device_get_iface (dev), ++ username ? username : "", ++ password ? password : "", ++ NULL); ++ } ++ return; ++ } ++ ++ g_return_if_fail (caller == SECRETS_CALLER_GSM); ++ g_return_if_fail (nm_device_get_state (dev) == NM_DEVICE_STATE_NEED_AUTH); ++ ++ for (iter = updated_settings; iter; iter = g_slist_next (iter)) { ++ const char *setting_name = (const char *) iter->data; ++ ++ if (!strcmp (setting_name, NM_SETTING_GSM_SETTING_NAME)) ++ found = TRUE; ++ else ++ nm_warning ("Ignoring updated secrets for setting '%s'.", setting_name); ++ } ++ ++ if (!found) ++ return; ++ ++ req = nm_device_get_act_request (dev); ++ g_assert (req); ++ ++ g_return_if_fail (nm_act_request_get_connection (req) == connection); ++ ++ nm_device_activate_schedule_stage1_device_prepare (dev); ++} ++ ++static const char * ++real_get_ppp_name (NMModemDevice *device, NMConnection *connection) ++{ ++ NMSettingGsm *s_gsm; ++ ++ s_gsm = (NMSettingGsm *) nm_connection_get_setting (connection, NM_TYPE_SETTING_GSM); ++ g_assert (s_gsm); ++ ++ return nm_setting_gsm_get_username (s_gsm); ++} ++ ++/*****************************************************************************/ ++ ++static void ++nm_gsm_modem_init (NMGsmModem *self) ++{ ++ nm_device_set_device_type (NM_DEVICE (self), NM_DEVICE_TYPE_GSM); ++} ++ ++static void ++nm_gsm_modem_class_init (NMGsmModemClass *klass) ++{ ++ GObjectClass *object_class = G_OBJECT_CLASS (klass); ++ NMDeviceClass *device_class = NM_DEVICE_CLASS (klass); ++ NMModemDeviceClass *modem_class = NM_MODEM_DEVICE_CLASS (klass); ++ ++ g_type_class_add_private (object_class, sizeof (NMGsmModemPrivate)); ++ ++ /* Virtual methods */ ++ device_class->get_best_auto_connection = real_get_best_auto_connection; ++ device_class->connection_secrets_updated = real_connection_secrets_updated; ++ device_class->act_stage1_prepare = real_act_stage1_prepare; ++ modem_class->get_ppp_name = real_get_ppp_name; ++ ++ dbus_g_object_type_install_info (G_TYPE_FROM_CLASS (klass), ++ &dbus_glib_nm_gsm_device_object_info); ++} +diff --git a/src/modem-manager/nm-gsm-modem.h b/src/modem-manager/nm-gsm-modem.h +new file mode 100644 +index 0000000..8df8265 +--- /dev/null ++++ b/src/modem-manager/nm-gsm-modem.h +@@ -0,0 +1,36 @@ ++/* -*- Mode: C; tab-width: 5; indent-tabs-mode: t; c-basic-offset: 5 -*- */ ++ ++#ifndef NM_GSM_MODEM_H ++#define NM_GSM_MODEM_H ++ ++#include <nm-modem-device.h> ++ ++G_BEGIN_DECLS ++ ++#define NM_TYPE_GSM_MODEM (nm_gsm_modem_get_type ()) ++#define NM_GSM_MODEM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_GSM_MODEM, NMGsmModem)) ++#define NM_GSM_MODEM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_GSM_MODEM, NMGsmModemClass)) ++#define NM_IS_GSM_MODEM(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_GSM_MODEM)) ++#define NM_IS_GSM_MODEM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_GSM_MODEM)) ++#define NM_GSM_MODEM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_GSM_MODEM, NMGsmModemClass)) ++ ++typedef struct { ++ NMModemDevice parent; ++} NMGsmModem; ++ ++typedef struct { ++ NMModemDeviceClass parent; ++ ++ /* Signals */ ++ void (*signal_quality) (NMGsmModem *modem, guint32 quality); ++} NMGsmModemClass; ++ ++GType nm_gsm_modem_get_type (void); ++ ++NMDevice *nm_gsm_modem_new (const char *path, ++ const char *data_device, ++ const char *driver); ++ ++G_END_DECLS ++ ++#endif /* NM_GSM_MODEM_H */ +diff --git a/src/modem-manager/nm-modem-device.c b/src/modem-manager/nm-modem-device.c +new file mode 100644 +index 0000000..1f49acc +--- /dev/null ++++ b/src/modem-manager/nm-modem-device.c +@@ -0,0 +1,457 @@ ++/* -*- Mode: C; tab-width: 5; indent-tabs-mode: t; c-basic-offset: 5 -*- */ ++ ++#include <string.h> ++#include "nm-modem-device.h" ++#include "nm-device-private.h" ++#include "nm-device-interface.h" ++#include "nm-dbus-manager.h" ++#include "nm-setting-connection.h" ++#include "nm-setting-gsm.h" ++#include "nm-setting-cdma.h" ++#include "nm-marshal.h" ++#include "nm-properties-changed-signal.h" ++#include "nm-modem-types.h" ++#include "nm-utils.h" ++#include "nm-serial-device-glue.h" ++ ++G_DEFINE_TYPE (NMModemDevice, nm_modem_device, NM_TYPE_DEVICE) ++ ++#define NM_MODEM_DEVICE_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_MODEM_DEVICE, NMModemDevicePrivate)) ++ ++enum { ++ PROP_0, ++ PROP_PATH, ++ ++ LAST_PROP ++}; ++ ++typedef struct { ++ NMDBusManager *dbus_mgr; ++ char *path; ++ DBusGProxy *proxy; ++ NMPPPManager *ppp_manager; ++ NMIP4Config *pending_ip4_config; ++ ++ guint state_to_disconnected_id; ++ ++ /* PPP stats */ ++ guint32 in_bytes; ++ guint32 out_bytes; ++} NMModemDevicePrivate; ++ ++enum { ++ PPP_STATS, ++ PROPERTIES_CHANGED, ++ ++ LAST_SIGNAL ++}; ++ ++static guint signals[LAST_SIGNAL] = { 0 }; ++ ++NMPPPManager * ++nm_modem_device_get_ppp_manager (NMModemDevice *device) ++{ ++ g_return_val_if_fail (NM_IS_MODEM_DEVICE (device), NULL); ++ ++ return NM_MODEM_DEVICE_GET_PRIVATE (device)->ppp_manager; ++} ++ ++DBusGProxy * ++nm_modem_device_get_proxy (NMModemDevice *device, ++ const char *interface) ++{ ++ ++ NMModemDevicePrivate *priv = NM_MODEM_DEVICE_GET_PRIVATE (device); ++ const char *current_iface; ++ ++ g_return_val_if_fail (NM_IS_MODEM_DEVICE (device), NULL); ++ ++ /* Default to the default interface. */ ++ if (interface == NULL) ++ interface = MM_DBUS_INTERFACE_MODEM; ++ ++ current_iface = dbus_g_proxy_get_interface (priv->proxy); ++ if (!current_iface || strcmp (current_iface, interface)) ++ dbus_g_proxy_set_interface (priv->proxy, interface); ++ ++ return priv->proxy; ++} ++ ++void ++nm_modem_device_connect (NMModemDevice *device, ++ const char *number) ++{ ++ g_return_if_fail (NM_IS_MODEM_DEVICE (device)); ++ ++ NM_MODEM_DEVICE_GET_CLASS (device)->connect (device, number); ++} ++ ++const char * ++nm_modem_device_get_ppp_name (NMModemDevice *device, ++ NMConnection *connection) ++{ ++ g_return_val_if_fail (NM_IS_MODEM_DEVICE (device), NULL); ++ g_return_val_if_fail (NM_IS_CONNECTION (connection), NULL); ++ ++ if (NM_MODEM_DEVICE_GET_CLASS (device)->get_ppp_name) ++ return NM_MODEM_DEVICE_GET_CLASS (device)->get_ppp_name (device, connection); ++ ++ return NULL; ++} ++ ++static void ++ppp_state_changed (NMPPPManager *ppp_manager, NMPPPStatus status, gpointer user_data) ++{ ++ NMDevice *device = NM_DEVICE (user_data); ++ ++ switch (status) { ++ case NM_PPP_STATUS_NETWORK: ++ nm_device_state_changed (device, NM_DEVICE_STATE_IP_CONFIG, NM_DEVICE_STATE_REASON_NONE); ++ break; ++ case NM_PPP_STATUS_DISCONNECT: ++ nm_device_state_changed (device, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_PPP_DISCONNECT); ++ break; ++ case NM_PPP_STATUS_DEAD: ++ nm_device_state_changed (device, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_PPP_FAILED); ++ break; ++ case NM_PPP_STATUS_AUTHENTICATE: ++ nm_device_state_changed (device, NM_DEVICE_STATE_NEED_AUTH, NM_DEVICE_STATE_REASON_NONE); ++ break; ++ default: ++ break; ++ } ++} ++ ++static void ++ppp_ip4_config (NMPPPManager *ppp_manager, ++ const char *iface, ++ NMIP4Config *config, ++ gpointer user_data) ++{ ++ NMDevice *device = NM_DEVICE (user_data); ++ ++ nm_device_set_ip_iface (device, iface); ++ NM_MODEM_DEVICE_GET_PRIVATE (device)->pending_ip4_config = g_object_ref (config); ++ nm_device_activate_schedule_stage4_ip_config_get (device); ++} ++ ++static void ++ppp_stats (NMPPPManager *ppp_manager, ++ guint32 in_bytes, ++ guint32 out_bytes, ++ gpointer user_data) ++{ ++ NMModemDevice *device = NM_MODEM_DEVICE (user_data); ++ NMModemDevicePrivate *priv = NM_MODEM_DEVICE_GET_PRIVATE (device); ++ ++ if (priv->in_bytes != in_bytes || priv->out_bytes != out_bytes) { ++ priv->in_bytes = in_bytes; ++ priv->out_bytes = out_bytes; ++ ++ g_signal_emit (device, signals[PPP_STATS], 0, in_bytes, out_bytes); ++ } ++} ++ ++static NMActStageReturn ++real_act_stage2_config (NMDevice *device, NMDeviceStateReason *reason) ++{ ++ NMModemDevicePrivate *priv = NM_MODEM_DEVICE_GET_PRIVATE (device); ++ NMActRequest *req; ++ const char *ppp_name = NULL; ++ GError *err = NULL; ++ NMActStageReturn ret; ++ ++ req = nm_device_get_act_request (device); ++ g_assert (req); ++ ++ ppp_name = nm_modem_device_get_ppp_name (NM_MODEM_DEVICE (device), ++ nm_act_request_get_connection (req)); ++ ++ priv->ppp_manager = nm_ppp_manager_new (nm_device_get_iface (device)); ++ if (nm_ppp_manager_start (priv->ppp_manager, req, ppp_name, &err)) { ++ g_signal_connect (priv->ppp_manager, "state-changed", ++ G_CALLBACK (ppp_state_changed), ++ device); ++ g_signal_connect (priv->ppp_manager, "ip4-config", ++ G_CALLBACK (ppp_ip4_config), ++ device); ++ g_signal_connect (priv->ppp_manager, "stats", ++ G_CALLBACK (ppp_stats), ++ device); ++ ++ ret = NM_ACT_STAGE_RETURN_POSTPONE; ++ } else { ++ nm_warning ("%s", err->message); ++ g_error_free (err); ++ ++ g_object_unref (priv->ppp_manager); ++ priv->ppp_manager = NULL; ++ ++ *reason = NM_DEVICE_STATE_REASON_PPP_START_FAILED; ++ ret = NM_ACT_STAGE_RETURN_FAILURE; ++ } ++ ++ return ret; ++} ++ ++static NMActStageReturn ++real_act_stage4_get_ip4_config (NMDevice *device, ++ NMIP4Config **config, ++ NMDeviceStateReason *reason) ++{ ++ NMModemDevicePrivate *priv = NM_MODEM_DEVICE_GET_PRIVATE (device); ++ ++ *config = priv->pending_ip4_config; ++ priv->pending_ip4_config = NULL; ++ ++ return NM_ACT_STAGE_RETURN_SUCCESS; ++} ++ ++static void ++real_deactivate_quickly (NMDevice *device) ++{ ++ NMModemDevicePrivate *priv = NM_MODEM_DEVICE_GET_PRIVATE (device); ++ ++ nm_device_set_ip_iface (device, NULL); ++ ++ if (priv->pending_ip4_config) { ++ g_object_unref (priv->pending_ip4_config); ++ priv->pending_ip4_config = NULL; ++ } ++ ++ priv->in_bytes = priv->out_bytes = 0; ++ ++ if (priv->ppp_manager) { ++ g_object_unref (priv->ppp_manager); ++ priv->ppp_manager = NULL; ++ } ++ ++ dbus_g_proxy_call_no_reply (nm_modem_device_get_proxy (NM_MODEM_DEVICE (device), NULL), ++ "Enable", G_TYPE_BOOLEAN, FALSE, G_TYPE_INVALID); ++} ++ ++static guint32 ++real_get_generic_capabilities (NMDevice *dev) ++{ ++ return NM_DEVICE_CAP_NM_SUPPORTED; ++} ++ ++ ++static void ++connect_done (DBusGProxy *proxy, DBusGProxyCall *call_id, gpointer user_data) ++{ ++ NMDevice *device = NM_DEVICE (user_data); ++ GError *error = NULL; ++ ++ if (dbus_g_proxy_end_call (proxy, call_id, &error, G_TYPE_INVALID)) ++ nm_device_activate_schedule_stage2_device_config (device); ++ else { ++ nm_warning ("Connect failed: %s", error->message); ++ g_error_free (error); ++ nm_device_state_changed (device, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_MODEM_DIAL_FAILED); ++ } ++} ++ ++static void ++real_connect (NMModemDevice *modem, const char *number) ++{ ++ dbus_g_proxy_begin_call_with_timeout (nm_modem_device_get_proxy (modem, MM_DBUS_INTERFACE_MODEM), ++ "Connect", connect_done, ++ modem, NULL, 60000, ++ G_TYPE_STRING, number ? number : "", ++ G_TYPE_INVALID); ++} ++ ++static gboolean ++unavailable_to_disconnected (gpointer user_data) ++{ ++ nm_device_state_changed (NM_DEVICE (user_data), ++ NM_DEVICE_STATE_DISCONNECTED, ++ NM_DEVICE_STATE_REASON_NONE); ++ return FALSE; ++} ++ ++static void ++device_state_changed (NMDeviceInterface *device, ++ NMDeviceState new_state, ++ NMDeviceState old_state, ++ NMDeviceStateReason reason, ++ gpointer user_data) ++{ ++ NMModemDevice *self = NM_MODEM_DEVICE (user_data); ++ NMModemDevicePrivate *priv = NM_MODEM_DEVICE_GET_PRIVATE (self); ++ ++ /* Remove any previous delayed transition to disconnected */ ++ if (priv->state_to_disconnected_id) { ++ g_source_remove (priv->state_to_disconnected_id); ++ priv->state_to_disconnected_id = 0; ++ } ++ ++ /* If transitioning to UNAVAILBLE and we have a carrier, transition to ++ * DISCONNECTED because the device is ready to use. Otherwise the carrier-on ++ * handler will handle the transition to DISCONNECTED when the carrier is detected. ++ */ ++ if (new_state == NM_DEVICE_STATE_UNAVAILABLE) ++ priv->state_to_disconnected_id = g_idle_add (unavailable_to_disconnected, user_data); ++ ++ /* Make sure we don't leave the serial device open */ ++ switch (new_state) { ++ case NM_DEVICE_STATE_NEED_AUTH: ++ if (priv->ppp_manager) ++ break; ++ /* else fall through */ ++ case NM_DEVICE_STATE_UNMANAGED: ++ case NM_DEVICE_STATE_UNAVAILABLE: ++ case NM_DEVICE_STATE_FAILED: ++ case NM_DEVICE_STATE_DISCONNECTED: ++ dbus_g_proxy_call_no_reply (nm_modem_device_get_proxy (self, NULL), ++ "Disconnect", G_TYPE_INVALID); ++ break; ++ default: ++ break; ++ } ++} ++ ++/*****************************************************************************/ ++ ++static void ++nm_modem_device_init (NMModemDevice *self) ++{ ++ NMModemDevicePrivate *priv = NM_MODEM_DEVICE_GET_PRIVATE (self); ++ ++ priv->dbus_mgr = nm_dbus_manager_get (); ++} ++ ++static GObject* ++constructor (GType type, ++ guint n_construct_params, ++ GObjectConstructParam *construct_params) ++{ ++ GObject *object; ++ NMModemDevicePrivate *priv; ++ ++ object = G_OBJECT_CLASS (nm_modem_device_parent_class)->constructor (type, ++ n_construct_params, ++ construct_params); ++ if (!object) ++ return NULL; ++ ++ priv = NM_MODEM_DEVICE_GET_PRIVATE (object); ++ ++ if (!priv->path) { ++ g_warning ("DBus path not provided"); ++ goto err; ++ } ++ ++ priv->proxy = dbus_g_proxy_new_for_name (nm_dbus_manager_get_connection (priv->dbus_mgr), ++ MM_DBUS_SERVICE, priv->path, MM_DBUS_INTERFACE_MODEM); ++ ++ g_signal_connect (object, "state-changed", G_CALLBACK (device_state_changed), object); ++ ++ return object; ++ ++ err: ++ g_object_unref (object); ++ return NULL; ++} ++ ++static void ++get_property (GObject *object, guint prop_id, ++ GValue *value, GParamSpec *pspec) ++{ ++ NMModemDevicePrivate *priv = NM_MODEM_DEVICE_GET_PRIVATE (object); ++ ++ switch (prop_id) { ++ case PROP_PATH: ++ g_value_set_string (value, priv->path); ++ break; ++ default: ++ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); ++ break; ++ } ++ ++} ++ ++static void ++set_property (GObject *object, guint prop_id, ++ const GValue *value, GParamSpec *pspec) ++{ ++ NMModemDevicePrivate *priv = NM_MODEM_DEVICE_GET_PRIVATE (object); ++ ++ switch (prop_id) { ++ case PROP_PATH: ++ /* Construct only */ ++ priv->path = g_value_dup_string (value); ++ break; ++ default: ++ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); ++ break; ++ } ++} ++ ++static void ++finalize (GObject *object) ++{ ++ NMModemDevicePrivate *priv = NM_MODEM_DEVICE_GET_PRIVATE (object); ++ ++ if (priv->state_to_disconnected_id) { ++ g_source_remove (priv->state_to_disconnected_id); ++ priv->state_to_disconnected_id = 0; ++ } ++ ++ if (priv->proxy) ++ g_object_unref (priv->proxy); ++ ++ g_object_unref (priv->dbus_mgr); ++ ++ G_OBJECT_CLASS (nm_modem_device_parent_class)->finalize (object); ++} ++ ++static void ++nm_modem_device_class_init (NMModemDeviceClass *klass) ++{ ++ GObjectClass *object_class = G_OBJECT_CLASS (klass); ++ NMDeviceClass *device_class = NM_DEVICE_CLASS (klass); ++ ++ g_type_class_add_private (object_class, sizeof (NMModemDevicePrivate)); ++ ++ /* Virtual methods */ ++ object_class->constructor = constructor; ++ object_class->set_property = set_property; ++ object_class->get_property = get_property; ++ object_class->finalize = finalize; ++ ++ device_class->get_generic_capabilities = real_get_generic_capabilities; ++ device_class->act_stage2_config = real_act_stage2_config; ++ device_class->act_stage4_get_ip4_config = real_act_stage4_get_ip4_config; ++ device_class->deactivate_quickly = real_deactivate_quickly; ++ ++ klass->connect = real_connect; ++ ++ /* Properties */ ++ g_object_class_install_property ++ (object_class, PROP_PATH, ++ g_param_spec_string (NM_MODEM_DEVICE_PATH, ++ "DBus path", ++ "DBus path", ++ NULL, ++ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); ++ ++ /* Signals */ ++ signals[PPP_STATS] = ++ g_signal_new ("ppp-stats", ++ G_OBJECT_CLASS_TYPE (object_class), ++ G_SIGNAL_RUN_FIRST, ++ G_STRUCT_OFFSET (NMModemDeviceClass, ppp_stats), ++ NULL, NULL, ++ _nm_marshal_VOID__UINT_UINT, ++ G_TYPE_NONE, 2, ++ G_TYPE_UINT, G_TYPE_UINT); ++ ++ signals[PROPERTIES_CHANGED] = ++ nm_properties_changed_signal_new (object_class, ++ G_STRUCT_OFFSET (NMModemDeviceClass, properties_changed)); ++ ++ dbus_g_object_type_install_info (G_TYPE_FROM_CLASS (klass), ++ &dbus_glib_nm_serial_device_object_info); ++} +diff --git a/src/modem-manager/nm-modem-device.h b/src/modem-manager/nm-modem-device.h +new file mode 100644 +index 0000000..fae6d74 +--- /dev/null ++++ b/src/modem-manager/nm-modem-device.h +@@ -0,0 +1,55 @@ ++/* -*- Mode: C; tab-width: 5; indent-tabs-mode: t; c-basic-offset: 5 -*- */ ++ ++#ifndef NM_MODEM_DEVICE_H ++#define NM_MODEM_DEVICE_H ++ ++#include <dbus/dbus-glib.h> ++#include <nm-device.h> ++#include "ppp-manager/nm-ppp-manager.h" ++ ++G_BEGIN_DECLS ++ ++#define NM_TYPE_MODEM_DEVICE (nm_modem_device_get_type ()) ++#define NM_MODEM_DEVICE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_MODEM_DEVICE, NMModemDevice)) ++#define NM_MODEM_DEVICE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_MODEM_DEVICE, NMModemDeviceClass)) ++#define NM_IS_MODEM_DEVICE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_MODEM_DEVICE)) ++#define NM_IS_MODEM_DEVICE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_MODEM_DEVICE)) ++#define NM_MODEM_DEVICE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_MODEM_DEVICE, NMModemDeviceClass)) ++ ++#define NM_MODEM_DEVICE_PATH "path" ++ ++typedef struct { ++ NMDevice parent; ++} NMModemDevice; ++ ++typedef struct { ++ NMDeviceClass parent; ++ ++ void (*connect) (NMModemDevice *device, ++ const char *number); ++ ++ const char *(*get_ppp_name) (NMModemDevice *device, ++ NMConnection *connection); ++ ++ /* Signals */ ++ void (*ppp_stats) (NMModemDevice *device, guint32 in_bytes, guint32 out_bytes); ++ void (*properties_changed) (NMModemDevice *device, GHashTable *properties); ++} NMModemDeviceClass; ++ ++GType nm_modem_device_get_type (void); ++ ++/* Protected */ ++ ++NMPPPManager *nm_modem_device_get_ppp_manager (NMModemDevice *device); ++DBusGProxy *nm_modem_device_get_proxy (NMModemDevice *device, ++ const char *interface); ++ ++void nm_modem_device_connect (NMModemDevice *device, ++ const char *number); ++ ++const char *nm_modem_device_get_ppp_name (NMModemDevice *device, ++ NMConnection *connection); ++ ++G_END_DECLS ++ ++#endif /* NM_MODEM_DEVICE_H */ +diff --git a/src/modem-manager/nm-modem-manager.c b/src/modem-manager/nm-modem-manager.c +new file mode 100644 +index 0000000..be0ca7a +--- /dev/null ++++ b/src/modem-manager/nm-modem-manager.c +@@ -0,0 +1,395 @@ ++/* -*- Mode: C; tab-width: 5; indent-tabs-mode: t; c-basic-offset: 5 -*- */ ++ ++#include <string.h> ++#include "nm-modem-manager.h" ++#include "nm-modem-device.h" ++#include "nm-gsm-modem.h" ++#include "nm-gsm-modem-hso.h" ++#include "nm-gsm-modem-mbm.h" ++#include "nm-cdma-modem.h" ++#include "nm-dbus-manager.h" ++#include "nm-utils.h" ++#include "nm-modem-types.h" ++ ++#define MODEM_POKE_INTERVAL 120000 ++ ++G_DEFINE_TYPE (NMModemManager, nm_modem_manager, G_TYPE_OBJECT) ++ ++#define NM_MODEM_MANAGER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_MODEM_MANAGER, NMModemManagerPrivate)) ++ ++typedef struct { ++ NMDBusManager *dbus_mgr; ++ DBusGProxy *proxy; ++ GHashTable *modems; ++ gboolean disposed; ++ guint poke_id; ++} NMModemManagerPrivate; ++ ++enum { ++ DEVICE_ADDED, ++ DEVICE_REMOVED, ++ ++ LAST_SIGNAL ++}; ++ ++static guint signals[LAST_SIGNAL] = { 0 }; ++ ++ ++NMModemManager * ++nm_modem_manager_get (void) ++{ ++ static NMModemManager *singleton = NULL; ++ ++ if (!singleton) ++ singleton = NM_MODEM_MANAGER (g_object_new (NM_TYPE_MODEM_MANAGER, NULL)); ++ else ++ g_object_ref (singleton); ++ ++ g_assert (singleton); ++ return singleton; ++} ++ ++static gboolean ++get_modem_properties (DBusGConnection *connection, ++ const char *path, ++ char **data_device, ++ char **driver, ++ guint32 *type) ++{ ++ DBusGProxy *proxy; ++ GValue value = { 0 }; ++ GError *err = NULL; ++ ++ proxy = dbus_g_proxy_new_for_name (connection, ++ MM_DBUS_SERVICE, ++ path, ++ "org.freedesktop.DBus.Properties"); ++ ++ if (dbus_g_proxy_call_with_timeout (proxy, "Get", 15000, &err, ++ G_TYPE_STRING, MM_DBUS_INTERFACE_MODEM, ++ G_TYPE_STRING, "Type", ++ G_TYPE_INVALID, ++ G_TYPE_VALUE, &value, ++ G_TYPE_INVALID)) { ++ *type = g_value_get_uint (&value); ++ g_value_unset (&value); ++ } else { ++ g_warning ("Could not get device type: %s", err->message); ++ goto out; ++ } ++ ++ if (dbus_g_proxy_call_with_timeout (proxy, "Get", 15000, &err, ++ G_TYPE_STRING, MM_DBUS_INTERFACE_MODEM, ++ G_TYPE_STRING, "DataDevice", ++ G_TYPE_INVALID, ++ G_TYPE_VALUE, &value, ++ G_TYPE_INVALID)) { ++ *data_device = g_value_dup_string (&value); ++ g_value_unset (&value); ++ } else { ++ g_warning ("Could not get modem data device: %s", err->message); ++ goto out; ++ } ++ ++ if (dbus_g_proxy_call_with_timeout (proxy, "Get", 15000, &err, ++ G_TYPE_STRING, MM_DBUS_INTERFACE_MODEM, ++ G_TYPE_STRING, "Driver", ++ G_TYPE_INVALID, ++ G_TYPE_VALUE, &value, ++ G_TYPE_INVALID)) { ++ *driver = g_value_dup_string (&value); ++ g_value_unset (&value); ++ } else { ++ g_warning ("Could not get modem driver: %s", err->message); ++ goto out; ++ } ++ ++ out: ++ if (err) ++ g_error_free (err); ++ ++ g_object_unref (proxy); ++ ++ return *data_device && *driver; ++} ++ ++static void ++create_modem (NMModemManager *manager, const char *path) ++{ ++ NMModemManagerPrivate *priv = NM_MODEM_MANAGER_GET_PRIVATE (manager); ++ NMDevice *device; ++ char *data_device = NULL; ++ char *driver = NULL; ++ uint modem_type = MM_MODEM_TYPE_UNKNOWN; ++ ++ if (g_hash_table_lookup (priv->modems, path)) { ++ nm_warning ("Modem with path %s already exists, ignoring", path); ++ return; ++ } ++ ++ if (!get_modem_properties (nm_dbus_manager_get_connection (priv->dbus_mgr), path, ++ &data_device, &driver, &modem_type)) ++ return; ++ ++ if (modem_type == MM_MODEM_TYPE_UNKNOWN) { ++ nm_warning ("Modem with path %s has unknown type, ignoring", path); ++ return; ++ } ++ ++ if (!driver || !strlen (driver)) { ++ nm_warning ("Modem with path %s has unknown driver, ignoring", path); ++ return; ++ } ++ ++ if (!data_device || !strlen (data_device)) { ++ nm_warning ("Modem with path %s has unknown data device, ignoring", path); ++ return; ++ } ++ ++ if (modem_type == MM_MODEM_TYPE_GSM) { ++ if (!strcmp (driver, "hso")) ++ device = nm_gsm_modem_hso_new (path, data_device, driver); ++ else if (!strcmp (driver, "mbm")) ++ device = nm_gsm_modem_mbm_new (path, data_device, driver); ++ else ++ device = nm_gsm_modem_new (path, data_device, driver); ++ } else if (modem_type == MM_MODEM_TYPE_CDMA) ++ device = nm_cdma_modem_new (path, data_device, driver); ++ else ++ g_error ("Invalid modem type"); ++ ++ g_free (data_device); ++ g_free (driver); ++ ++ if (device) { ++ g_hash_table_insert (priv->modems, g_strdup (path), device); ++ g_signal_emit (manager, signals[DEVICE_ADDED], 0, device); ++ } ++} ++ ++static void ++modem_added (DBusGProxy *proxy, const char *path, gpointer user_data) ++{ ++ create_modem (NM_MODEM_MANAGER (user_data), path); ++} ++ ++static void ++modem_removed (DBusGProxy *proxy, const char *path, gpointer user_data) ++{ ++ NMModemManagerPrivate *priv = NM_MODEM_MANAGER_GET_PRIVATE (user_data); ++ NMModemDevice *modem; ++ ++ modem = (NMModemDevice *) g_hash_table_lookup (priv->modems, path); ++ if (modem) { ++ g_signal_emit (user_data, signals[DEVICE_REMOVED], 0, modem); ++ g_hash_table_remove (priv->modems, path); ++ } ++} ++ ++static gboolean ++poke_modem_cb (gpointer user_data) ++{ ++ NMModemManager *self = NM_MODEM_MANAGER (user_data); ++ NMModemManagerPrivate *priv = NM_MODEM_MANAGER_GET_PRIVATE (self); ++ DBusGConnection *g_connection; ++ DBusGProxy *proxy; ++ ++ g_connection = nm_dbus_manager_get_connection (priv->dbus_mgr); ++ proxy = dbus_g_proxy_new_for_name (g_connection, ++ MM_DBUS_SERVICE, ++ MM_DBUS_PATH, ++ MM_DBUS_INTERFACE); ++ ++ nm_info ("Trying to start the modem-manager..."); ++ dbus_g_proxy_call_no_reply (proxy, "EnumerateDevices", G_TYPE_INVALID); ++ g_object_unref (proxy); ++ ++ return TRUE; ++} ++ ++static void ++enumerate_devices_done (DBusGProxy *proxy, DBusGProxyCall *call_id, gpointer data) ++{ ++ NMModemManager *manager = NM_MODEM_MANAGER (data); ++ GPtrArray *modems; ++ GError *error = NULL; ++ ++ if (!dbus_g_proxy_end_call (proxy, call_id, &error, ++ dbus_g_type_get_collection ("GPtrArray", DBUS_TYPE_G_OBJECT_PATH), &modems, ++ G_TYPE_INVALID)) { ++ nm_warning ("Could not get modem list: %s", error->message); ++ g_error_free (error); ++ } else { ++ int i; ++ ++ for (i = 0; i < modems->len; i++) { ++ char *path = (char *) g_ptr_array_index (modems, i); ++ ++ create_modem (manager, path); ++ g_free (path); ++ } ++ ++ g_ptr_array_free (modems, TRUE); ++ } ++} ++ ++static void ++modem_manager_appeared (NMModemManager *self, gboolean enumerate_devices) ++{ ++ NMModemManagerPrivate *priv = NM_MODEM_MANAGER_GET_PRIVATE (self); ++ ++ if (priv->poke_id) { ++ g_source_remove (priv->poke_id); ++ priv->poke_id = 0; ++ } ++ ++ priv->proxy = dbus_g_proxy_new_for_name (nm_dbus_manager_get_connection (priv->dbus_mgr), ++ MM_DBUS_SERVICE, MM_DBUS_PATH, MM_DBUS_INTERFACE); ++ ++ dbus_g_proxy_add_signal (priv->proxy, "DeviceAdded", G_TYPE_STRING, G_TYPE_INVALID); ++ dbus_g_proxy_connect_signal (priv->proxy, "DeviceAdded", ++ G_CALLBACK (modem_added), self, ++ NULL); ++ ++ dbus_g_proxy_add_signal (priv->proxy, "DeviceRemoved", G_TYPE_STRING, G_TYPE_INVALID); ++ dbus_g_proxy_connect_signal (priv->proxy, "DeviceRemoved", ++ G_CALLBACK (modem_removed), self, ++ NULL); ++ ++ if (enumerate_devices) ++ dbus_g_proxy_begin_call (priv->proxy, "EnumerateDevices", enumerate_devices_done, self, NULL, G_TYPE_INVALID); ++} ++ ++static gboolean ++remove_one_modem (gpointer key, gpointer value, gpointer user_data) ++{ ++ g_signal_emit (user_data, signals[DEVICE_REMOVED], 0, value); ++ ++ return TRUE; ++} ++ ++static void ++modem_manager_disappeared (NMModemManager *self) ++{ ++ NMModemManagerPrivate *priv = NM_MODEM_MANAGER_GET_PRIVATE (self); ++ ++ g_hash_table_foreach_remove (priv->modems, remove_one_modem, self); ++ ++ if (priv->proxy) { ++ g_object_unref (priv->proxy); ++ priv->proxy = NULL; ++ } ++ ++ /* Try to activate the modem-manager */ ++ poke_modem_cb (self); ++ priv->poke_id = g_timeout_add (MODEM_POKE_INTERVAL, poke_modem_cb, self); ++} ++ ++static void ++nm_modem_manager_name_owner_changed (NMDBusManager *dbus_mgr, ++ const char *name, ++ const char *old_owner, ++ const char *new_owner, ++ gpointer user_data) ++{ ++ gboolean old_owner_good; ++ gboolean new_owner_good; ++ ++ /* Can't handle the signal if its not from the modem service */ ++ if (strcmp (MM_DBUS_SERVICE, name) != 0) ++ return; ++ ++ old_owner_good = (old_owner && strlen (old_owner)); ++ new_owner_good = (new_owner && strlen (new_owner)); ++ ++ if (!old_owner_good && new_owner_good) { ++ nm_info ("modem manager appeared"); ++ modem_manager_appeared (NM_MODEM_MANAGER (user_data), FALSE); ++ } else if (old_owner_good && !new_owner_good) { ++ nm_info ("modem manager disappeared"); ++ modem_manager_disappeared (NM_MODEM_MANAGER (user_data)); ++ } ++} ++ ++/*******************************************************/ ++ ++static void ++nm_modem_manager_init (NMModemManager *self) ++{ ++ NMModemManagerPrivate *priv = NM_MODEM_MANAGER_GET_PRIVATE (self); ++ ++ priv->modems = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref); ++ priv->dbus_mgr = nm_dbus_manager_get (); ++ ++ g_signal_connect (priv->dbus_mgr, "name-owner-changed", ++ G_CALLBACK (nm_modem_manager_name_owner_changed), ++ self); ++ ++ if (nm_dbus_manager_name_has_owner (priv->dbus_mgr, MM_DBUS_SERVICE)) ++ modem_manager_appeared (self, TRUE); ++ else ++ modem_manager_disappeared (self); ++} ++ ++static void ++dispose (GObject *object) ++{ ++ NMModemManagerPrivate *priv = NM_MODEM_MANAGER_GET_PRIVATE (object); ++ ++ if (priv->disposed) ++ return; ++ ++ priv->disposed = TRUE; ++ ++ if (priv->poke_id) { ++ g_source_remove (priv->poke_id); ++ priv->poke_id = 0; ++ } ++ ++ g_hash_table_foreach_remove (priv->modems, remove_one_modem, object); ++ g_hash_table_destroy (priv->modems); ++ ++ if (priv->proxy) { ++ g_object_unref (priv->proxy); ++ priv->proxy = NULL; ++ } ++ ++ if (priv->dbus_mgr) { ++ g_object_unref (priv->dbus_mgr); ++ priv->dbus_mgr = NULL; ++ } ++ ++ /* Chain up to the parent class */ ++ G_OBJECT_CLASS (nm_modem_manager_parent_class)->dispose (object); ++} ++ ++static void ++nm_modem_manager_class_init (NMModemManagerClass *klass) ++{ ++ GObjectClass *object_class = G_OBJECT_CLASS (klass); ++ ++ g_type_class_add_private (object_class, sizeof (NMModemManagerPrivate)); ++ ++ object_class->dispose = dispose; ++ ++ /* signals */ ++ signals[DEVICE_ADDED] = ++ g_signal_new ("device-added", ++ G_OBJECT_CLASS_TYPE (object_class), ++ G_SIGNAL_RUN_FIRST, ++ G_STRUCT_OFFSET (NMModemManagerClass, device_added), ++ NULL, NULL, ++ g_cclosure_marshal_VOID__OBJECT, ++ G_TYPE_NONE, 1, ++ G_TYPE_OBJECT); ++ ++ signals[DEVICE_REMOVED] = ++ g_signal_new ("device-removed", ++ G_OBJECT_CLASS_TYPE (object_class), ++ G_SIGNAL_RUN_FIRST, ++ G_STRUCT_OFFSET (NMModemManagerClass, device_removed), ++ NULL, NULL, ++ g_cclosure_marshal_VOID__OBJECT, ++ G_TYPE_NONE, 1, ++ G_TYPE_OBJECT); ++} +diff --git a/src/modem-manager/nm-modem-manager.h b/src/modem-manager/nm-modem-manager.h +new file mode 100644 +index 0000000..ec62f84 +--- /dev/null ++++ b/src/modem-manager/nm-modem-manager.h +@@ -0,0 +1,35 @@ ++/* -*- Mode: C; tab-width: 5; indent-tabs-mode: t; c-basic-offset: 5 -*- */ ++ ++#ifndef NM_MODEM_MANAGER_H ++#define NM_MODEM_MANAGER_H ++ ++#include <glib-object.h> ++#include "nm-device.h" ++ ++#define NM_TYPE_MODEM_MANAGER (nm_modem_manager_get_type ()) ++#define NM_MODEM_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_MODEM_MANAGER, NMModemManager)) ++#define NM_MODEM_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_MODEM_MANAGER, NMModemManagerClass)) ++#define NM_IS_MODEM_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_MODEM_MANAGER)) ++#define NM_IS_MODEM_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_MODEM_MANAGER)) ++#define NM_MODEM_MANAGER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_MODEM_MANAGER, NMModemManagerClass)) ++ ++typedef struct { ++ GObject parent; ++} NMModemManager; ++ ++typedef struct { ++ GObjectClass parent; ++ ++ /* Signals */ ++ void (*device_added) (NMModemManager *manager, ++ NMDevice *device); ++ ++ void (*device_removed) (NMModemManager *manager, ++ NMDevice *device); ++} NMModemManagerClass; ++ ++GType nm_modem_manager_get_type (void); ++ ++NMModemManager *nm_modem_manager_get (void); ++ ++#endif /* NM_MODEM_MANAGER_H */ +diff --git a/src/modem-manager/nm-modem-types.h b/src/modem-manager/nm-modem-types.h +new file mode 100644 +index 0000000..de1d2de +--- /dev/null ++++ b/src/modem-manager/nm-modem-types.h +@@ -0,0 +1,90 @@ ++/* -*- Mode: C; tab-width: 5; indent-tabs-mode: t; c-basic-offset: 5 -*- */ ++ ++#ifndef NM_MODEM_TYPES_H ++#define NM_MODEM_TYPES_H ++ ++#define MM_DBUS_SERVICE "org.freedesktop.ModemManager" ++#define MM_DBUS_PATH "/org/freedesktop/ModemManager" ++#define MM_DBUS_INTERFACE "org.freedesktop.ModemManager" ++#define MM_DBUS_INTERFACE_MODEM "org.freedesktop.ModemManager.Modem" ++#define MM_DBUS_INTERFACE_MODEM_CDMA "org.freedesktop.ModemManager.Modem.Cdma" ++ ++#define MM_DBUS_INTERFACE_MODEM_GSM_CARD "org.freedesktop.ModemManager.Modem.Gsm.Card" ++#define MM_DBUS_INTERFACE_MODEM_GSM_NETWORK "org.freedesktop.ModemManager.Modem.Gsm.Network" ++#define MM_DBUS_INTERFACE_MODEM_GSM_HSO "org.freedesktop.ModemManager.Modem.Gsm.Hso" ++#define MM_DBUS_INTERFACE_MODEM_GSM_MBM "org.freedesktop.ModemManager.Modem.Gsm.Mbm" ++ ++#define MM_MODEM_TYPE_UNKNOWN 0 ++#define MM_MODEM_TYPE_GSM 1 ++#define MM_MODEM_TYPE_CDMA 2 ++ ++/* Errors */ ++ ++#define MM_SERIAL_OPEN_FAILED MM_DBUS_INTERFACE_MODEM ".SerialOpenFailed" ++#define MM_SERIAL_SEND_FAILED MM_DBUS_INTERFACE_MODEM ".SerialSendFailed" ++#define MM_SERIAL_RESPONSE_TIMEOUT MM_DBUS_INTERFACE_MODEM ".SerialResponseTimeout" ++ ++#define MM_MODEM_ERROR_GENERAL MM_DBUS_INTERFACE_MODEM ".General" ++#define MM_MODEM_ERROR_OPERATION_NOT_SUPPORTED MM_DBUS_INTERFACE_MODEM ".OperationNotSupported" ++ ++#define MM_MODEM_CONNECT_ERROR_NO_CARRIER MM_DBUS_INTERFACE_MODEM ".NoCarrier" ++#define MM_MODEM_CONNECT_ERROR_NO_DIALTONE MM_DBUS_INTERFACE_MODEM ".NoDialtone" ++#define MM_MODEM_CONNECT_ERROR_BUSY MM_DBUS_INTERFACE_MODEM ".Busy" ++#define MM_MODEM_CONNECT_ERROR_NO_ANSWER MM_DBUS_INTERFACE_MODEM ".NoAnswer" ++ ++#define MM_MODEM_ERROR "org.freedesktop.ModemManager.Modem.Gsm" ++ ++#define MM_MODEM_ERROR_PHONE_FAILURE MM_MODEM_ERROR ".PhoneFailure" ++#define MM_MODEM_ERROR_NO_CONNECTION MM_MODEM_ERROR ".NoConnection" ++#define MM_MODEM_ERROR_LINK_RESERVED MM_MODEM_ERROR ".LinkReserved" ++#define MM_MODEM_ERROR_NOT_ALLOWED MM_MODEM_ERROR ".OperationNotAllowed" ++#define MM_MODEM_ERROR_NOT_SUPPORTED MM_MODEM_ERROR ".OperationNotSupported" ++#define MM_MODEM_ERROR_PH_SIM_PIN MM_MODEM_ERROR ".PhSimPinRequired" ++#define MM_MODEM_ERROR_PH_FSIM_PIN MM_MODEM_ERROR ".PhFSimPinRequired" ++#define MM_MODEM_ERROR_PH_FSIM_PUK MM_MODEM_ERROR ".PhFPukRequired" ++#define MM_MODEM_ERROR_SIM_NOT_INSERTED MM_MODEM_ERROR ".SimNotInserted" ++#define MM_MODEM_ERROR_SIM_PIN MM_MODEM_ERROR ".SimPinRequired" ++#define MM_MODEM_ERROR_SIM_PUK MM_MODEM_ERROR ".SimPukRequired" ++#define MM_MODEM_ERROR_SIM_FAILURE MM_MODEM_ERROR ".SimFailure" ++#define MM_MODEM_ERROR_SIM_BUSY MM_MODEM_ERROR ".SimBusy" ++#define MM_MODEM_ERROR_SIM_WRONG MM_MODEM_ERROR ".SimWrong" ++#define MM_MODEM_ERROR_WRONG_PASSWORD MM_MODEM_ERROR ".IncorrectPassword" ++#define MM_MODEM_ERROR_SIM_PIN2 MM_MODEM_ERROR ".SimPin2Required" ++#define MM_MODEM_ERROR_SIM_PUK2 MM_MODEM_ERROR ".SimPuk2Required" ++#define MM_MODEM_ERROR_MEMORY_FULL MM_MODEM_ERROR ".MemoryFull" ++#define MM_MODEM_ERROR_INVALID_INDEX MM_MODEM_ERROR ".InvalidIndex" ++#define MM_MODEM_ERROR_NOT_FOUND MM_MODEM_ERROR ".NotFound" ++#define MM_MODEM_ERROR_MEMORY_FAILURE MM_MODEM_ERROR ".MemoryFailure" ++#define MM_MODEM_ERROR_TEXT_TOO_LONG MM_MODEM_ERROR ".TextTooLong" ++#define MM_MODEM_ERROR_INVALID_CHARS MM_MODEM_ERROR ".InvalidChars" ++#define MM_MODEM_ERROR_DIAL_STRING_TOO_LONG MM_MODEM_ERROR ".DialStringTooLong" ++#define MM_MODEM_ERROR_DIAL_STRING_INVALID MM_MODEM_ERROR ".InvalidDialString" ++#define MM_MODEM_ERROR_NO_NETWORK MM_MODEM_ERROR ".NoNetwork" ++#define MM_MODEM_ERROR_NETWORK_TIMEOUT MM_MODEM_ERROR ".NetworkTimeout" ++#define MM_MODEM_ERROR_NETWORK_NOT_ALLOWED MM_MODEM_ERROR ".NetworkNotAllowed" ++#define MM_MODEM_ERROR_NETWORK_PIN MM_MODEM_ERROR ".NetworkPinRequired" ++#define MM_MODEM_ERROR_NETWORK_PUK MM_MODEM_ERROR ".NetworkPukRequired" ++#define MM_MODEM_ERROR_NETWORK_SUBSET_PIN MM_MODEM_ERROR ".NetworkSubsetPinRequired" ++#define MM_MODEM_ERROR_NETWORK_SUBSET_PUK MM_MODEM_ERROR ".NetworkSubsetPukRequired" ++#define MM_MODEM_ERROR_SERVICE_PIN MM_MODEM_ERROR ".ServicePinRequired" ++#define MM_MODEM_ERROR_SERVICE_PUK MM_MODEM_ERROR ".ServicePukRequired" ++#define MM_MODEM_ERROR_CORP_PIN MM_MODEM_ERROR ".CorporatePinRequired" ++#define MM_MODEM_ERROR_CORP_PUK MM_MODEM_ERROR ".CorporatePukRequired" ++#define MM_MODEM_ERROR_HIDDEN_KEY MM_MODEM_ERROR ".HiddenKeyRequired" ++#define MM_MODEM_ERROR_EAP_NOT_SUPPORTED MM_MODEM_ERROR ".EapMethodNotSupported" ++#define MM_MODEM_ERROR_INCORRECT_PARAMS MM_MODEM_ERROR ".IncorrectParams" ++#define MM_MODEM_ERROR_UNKNOWN MM_MODEM_ERROR ".Unknown" ++#define MM_MODEM_ERROR_GPRS_ILLEGAL_MS MM_MODEM_ERROR ".GprsIllegalMs" ++#define MM_MODEM_ERROR_GPRS_ILLEGAL_ME MM_MODEM_ERROR ".GprsIllegalMe" ++#define MM_MODEM_ERROR_GPRS_SERVICE_NOT_ALLOWED MM_MODEM_ERROR ".GprsServiceNotAllowed" ++#define MM_MODEM_ERROR_GPRS_PLMN_NOT_ALLOWED MM_MODEM_ERROR ".GprsPlmnNotAllowed" ++#define MM_MODEM_ERROR_GPRS_LOCATION_NOT_ALLOWED MM_MODEM_ERROR ".GprsLocationNotAllowed" ++#define MM_MODEM_ERROR_GPRS_ROAMING_NOT_ALLOWED MM_MODEM_ERROR ".GprsRoamingNotAllowed" ++#define MM_MODEM_ERROR_GPRS_OPTION_NOT_SUPPORTED MM_MODEM_ERROR ".GprsOptionNotSupported" ++#define MM_MODEM_ERROR_GPRS_NOT_SUBSCRIBED MM_MODEM_ERROR ".GprsNotSubscribed" ++#define MM_MODEM_ERROR_GPRS_OUT_OF_ORDER MM_MODEM_ERROR ".GprsOutOfOrder" ++#define MM_MODEM_ERROR_GPRS_PDP_AUTH_FAILURE MM_MODEM_ERROR ".GprsPdpAuthFailure" ++#define MM_MODEM_ERROR_GPRS_UNKNOWN MM_MODEM_ERROR ".GprsUnspecified" ++#define MM_MODEM_ERROR_GPRS_INVALID_CLASS MM_MODEM_ERROR ".GprsInvalidClass" ++ ++#endif /* NM_MODEM_TYPES_H */ +diff --git a/src/nm-cdma-device.c b/src/nm-cdma-device.c +deleted file mode 100644 +index c1c9b06..0000000 +--- a/src/nm-cdma-device.c ++++ /dev/null +@@ -1,593 +0,0 @@ +-/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +-/* NetworkManager -- Network link manager +- * +- * 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. +- * +- * 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., +- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +- * +- * Copyright (C) 2008 Red Hat, Inc. +- * Copyright (C) 2008 Novell, Inc. +- */ +- +-#include <stdio.h> +-#include <string.h> +-#include "nm-cdma-device.h" +-#include "nm-device-interface.h" +-#include "nm-device-private.h" +-#include "nm-setting-cdma.h" +-#include "nm-utils.h" +-#include "nm-properties-changed-signal.h" +-#include "nm-cdma-device-glue.h" +-#include "nm-setting-connection.h" +- +-G_DEFINE_TYPE (NMCdmaDevice, nm_cdma_device, NM_TYPE_SERIAL_DEVICE) +- +-#define NM_CDMA_DEVICE_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_CDMA_DEVICE, NMCdmaDevicePrivate)) +- +-typedef struct { +- char *monitor_iface; +- NMSerialDevice *monitor_device; +- +- guint state_to_disconnected_id; +-} NMCdmaDevicePrivate; +- +-enum { +- PROP_0, +- PROP_MONITOR_IFACE, +- +- LAST_PROP +-}; +- +-enum { +- PROPERTIES_CHANGED, +- +- LAST_SIGNAL +-}; +- +-static guint signals[LAST_SIGNAL] = { 0 }; +- +- +-NMCdmaDevice * +-nm_cdma_device_new (const char *udi, +- const char *data_iface, +- const char *monitor_iface, +- const char *driver, +- gboolean managed) +-{ +- g_return_val_if_fail (udi != NULL, NULL); +- g_return_val_if_fail (data_iface != NULL, NULL); +- g_return_val_if_fail (driver != NULL, NULL); +- +- return (NMCdmaDevice *) g_object_new (NM_TYPE_CDMA_DEVICE, +- NM_DEVICE_INTERFACE_UDI, udi, +- NM_DEVICE_INTERFACE_IFACE, data_iface, +- NM_DEVICE_INTERFACE_DRIVER, driver, +- NM_CDMA_DEVICE_MONITOR_IFACE, monitor_iface, +- NM_DEVICE_INTERFACE_MANAGED, managed, +- NULL); +-} +- +-static NMSetting * +-cdma_device_get_setting (NMCdmaDevice *device, GType setting_type) +-{ +- NMActRequest *req; +- NMSetting *setting = NULL; +- +- req = nm_device_get_act_request (NM_DEVICE (device)); +- if (req) { +- NMConnection *connection; +- +- connection = nm_act_request_get_connection (req); +- if (connection) +- setting = nm_connection_get_setting (connection, setting_type); +- } +- +- return setting; +-} +- +-static void +-dial_done (NMSerialDevice *device, +- int reply_index, +- const char *reply, +- gpointer user_data) +-{ +- NMDeviceStateReason reason = NM_DEVICE_STATE_REASON_UNKNOWN; +- gboolean success = FALSE; +- +- switch (reply_index) { +- case 0: +- nm_info ("Connected, Woo!"); +- success = TRUE; +- break; +- case 1: +- nm_info ("Busy"); +- reason = NM_DEVICE_STATE_REASON_MODEM_BUSY; +- break; +- case 2: +- nm_warning ("No dial tone"); +- reason = NM_DEVICE_STATE_REASON_MODEM_NO_DIAL_TONE; +- break; +- case 3: +- nm_warning ("No carrier"); +- reason = NM_DEVICE_STATE_REASON_MODEM_NO_CARRIER; +- break; +- case -1: +- nm_warning ("Dialing timed out"); +- reason = NM_DEVICE_STATE_REASON_MODEM_DIAL_TIMEOUT; +- break; +- default: +- nm_warning ("Dialing failed"); +- break; +- } +- +- if (success) +- nm_device_activate_schedule_stage2_device_config (NM_DEVICE (device)); +- else +- nm_device_state_changed (NM_DEVICE (device), NM_DEVICE_STATE_FAILED, reason); +-} +- +-static void +-do_dial (NMSerialDevice *device) +-{ +- NMSettingCdma *setting; +- char *command; +- guint id = 0; +- const char *responses[] = { "CONNECT", "BUSY", "NO DIAL TONE", "NO CARRIER", NULL }; +- +- setting = NM_SETTING_CDMA (cdma_device_get_setting (NM_CDMA_DEVICE (device), NM_TYPE_SETTING_CDMA)); +- +- command = g_strconcat ("ATDT", nm_setting_cdma_get_number (setting), NULL); +- if (nm_serial_device_send_command_string (device, command)) +- id = nm_serial_device_wait_for_reply (device, 60, responses, responses, dial_done, NULL); +- g_free (command); +- +- if (id == 0) +- nm_device_state_changed (NM_DEVICE (device), +- NM_DEVICE_STATE_FAILED, +- NM_DEVICE_STATE_REASON_UNKNOWN); +-} +- +-static void +-power_up_response (NMSerialDevice *device, +- int reply_index, +- const char *reply, +- gpointer user_data) +-{ +- /* Ignore errors */ +- do_dial (device); +-} +- +-static void +-power_up (NMSerialDevice *device) +-{ +- const char *responses[] = { "OK", "ERROR", "ERR", NULL }; +- guint id = 0; +- +- /* Only works on Sierra cards */ +- nm_info ("(%s): powering up...", nm_device_get_iface (NM_DEVICE (device))); +- if (nm_serial_device_send_command_string (device, "at!pcstate=1")) +- id = nm_serial_device_wait_for_reply (device, 10, responses, responses, power_up_response, NULL); +- +- /* Ignore errors */ +- if (id == 0) +- do_dial (device); +-} +- +-static void +-init_done (NMSerialDevice *device, +- int reply_index, +- const char *reply, +- gpointer user_data) +-{ +- switch (reply_index) { +- case 0: +- power_up (device); +- break; +- case -1: +- nm_warning ("Modem initialization timed out"); +- nm_device_state_changed (NM_DEVICE (device), +- NM_DEVICE_STATE_FAILED, +- NM_DEVICE_STATE_REASON_MODEM_INIT_FAILED); +- break; +- default: +- nm_warning ("Modem initialization failed"); +- nm_device_state_changed (NM_DEVICE (device), +- NM_DEVICE_STATE_FAILED, +- NM_DEVICE_STATE_REASON_MODEM_INIT_FAILED); +- return; +- } +-} +- +-static void +-init_modem (NMSerialDevice *device, gpointer user_data) +-{ +- guint id = 0; +- const char *responses[] = { "OK", "ERROR", "ERR", NULL }; +- +- if (nm_serial_device_send_command_string (device, "ATZ E0")) +- id = nm_serial_device_wait_for_reply (device, 10, responses, responses, init_done, NULL); +- +- if (id == 0) +- nm_device_state_changed (NM_DEVICE (device), NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_UNKNOWN); +-} +- +-static NMActStageReturn +-real_act_stage1_prepare (NMDevice *device, NMDeviceStateReason *reason) +-{ +- NMSerialDevice *serial_device = NM_SERIAL_DEVICE (device); +- NMSettingSerial *setting; +- guint id; +- +- setting = NM_SETTING_SERIAL (cdma_device_get_setting (NM_CDMA_DEVICE (device), NM_TYPE_SETTING_SERIAL)); +- +- if (!nm_serial_device_open (serial_device, setting)) { +- *reason = NM_DEVICE_STATE_REASON_CONFIG_FAILED; +- return NM_ACT_STAGE_RETURN_FAILURE; +- } +- +- id = nm_serial_device_flash (serial_device, 100, init_modem, NULL); +- if (!id) +- *reason = NM_DEVICE_STATE_REASON_UNKNOWN; +- +- return id ? NM_ACT_STAGE_RETURN_POSTPONE : NM_ACT_STAGE_RETURN_FAILURE; +-} +- +-static NMConnection * +-real_get_best_auto_connection (NMDevice *dev, +- GSList *connections, +- char **specific_object) +-{ +- GSList *iter; +- +- for (iter = connections; iter; iter = g_slist_next (iter)) { +- NMConnection *connection = NM_CONNECTION (iter->data); +- NMSettingConnection *s_con; +- +- s_con = (NMSettingConnection *) nm_connection_get_setting (connection, NM_TYPE_SETTING_CONNECTION); +- g_assert (s_con); +- +- if (!nm_setting_connection_get_autoconnect (s_con)) +- continue; +- +- if (strcmp (nm_setting_connection_get_connection_type (s_con), NM_SETTING_CDMA_SETTING_NAME)) +- continue; +- +- return connection; +- } +- return NULL; +-} +- +-static guint32 +-real_get_generic_capabilities (NMDevice *dev) +-{ +- return NM_DEVICE_CAP_NM_SUPPORTED; +-} +- +-static void +-real_connection_secrets_updated (NMDevice *dev, +- NMConnection *connection, +- GSList *updated_settings, +- RequestSecretsCaller caller) +-{ +- NMActRequest *req; +- gboolean found = FALSE; +- GSList *iter; +- +- if (caller == SECRETS_CALLER_PPP) { +- NMPPPManager *ppp_manager; +- NMSettingCdma *s_cdma = NULL; +- +- ppp_manager = nm_serial_device_get_ppp_manager (NM_SERIAL_DEVICE (dev)); +- g_return_if_fail (ppp_manager != NULL); +- +- s_cdma = (NMSettingCdma *) nm_connection_get_setting (connection, NM_TYPE_SETTING_CDMA); +- if (!s_cdma) { +- /* Shouldn't ever happen */ +- nm_ppp_manager_update_secrets (ppp_manager, +- nm_device_get_iface (dev), +- NULL, +- NULL, +- "missing CDMA setting; no secrets could be found."); +- } else { +- const char *cdma_username = nm_setting_cdma_get_username (s_cdma); +- const char *cdma_password = nm_setting_cdma_get_password (s_cdma); +- +- nm_ppp_manager_update_secrets (ppp_manager, +- nm_device_get_iface (dev), +- cdma_username ? cdma_username : "", +- cdma_password ? cdma_password : "", +- NULL); +- } +- return; +- } +- +- g_return_if_fail (caller == SECRETS_CALLER_CDMA); +- g_return_if_fail (nm_device_get_state (dev) == NM_DEVICE_STATE_NEED_AUTH); +- +- for (iter = updated_settings; iter; iter = g_slist_next (iter)) { +- const char *setting_name = (const char *) iter->data; +- +- if (!strcmp (setting_name, NM_SETTING_CDMA_SETTING_NAME)) +- found = TRUE; +- else +- nm_warning ("Ignoring updated secrets for setting '%s'.", setting_name); +- } +- +- if (!found) +- return; +- +- req = nm_device_get_act_request (dev); +- g_assert (req); +- +- g_return_if_fail (nm_act_request_get_connection (req) == connection); +- +- nm_device_activate_schedule_stage1_device_prepare (dev); +-} +- +-static const char * +-real_get_ppp_name (NMSerialDevice *device, NMActRequest *req) +-{ +- NMConnection *connection; +- NMSettingCdma *s_cdma; +- +- connection = nm_act_request_get_connection (req); +- g_assert (connection); +- +- s_cdma = (NMSettingCdma *) nm_connection_get_setting (connection, NM_TYPE_SETTING_CDMA); +- g_assert (s_cdma); +- +- return nm_setting_cdma_get_username (s_cdma); +-} +- +-/*****************************************************************************/ +-/* Monitor device handling */ +- +-static gboolean +-monitor_device_got_data (GIOChannel *source, +- GIOCondition condition, +- gpointer data) +-{ +- gsize bytes_read; +- char buf[4096]; +- GIOStatus status; +- +- if (condition & G_IO_IN) { +- do { +- status = g_io_channel_read_chars (source, buf, 4096, &bytes_read, NULL); +- +- if (bytes_read) { +- buf[bytes_read] = '\0'; +- /* Do nothing with the data for now */ +- nm_debug ("Monitor got unhandled data: '%s'", buf); +- } +- } while (bytes_read == 4096 || status == G_IO_STATUS_AGAIN); +- } +- +- if (condition & G_IO_HUP || condition & G_IO_ERR) { +- return FALSE; +- } +- +- return TRUE; +-} +- +-static gboolean +-setup_monitor_device (NMCdmaDevice *device) +-{ +- NMCdmaDevicePrivate *priv = NM_CDMA_DEVICE_GET_PRIVATE (device); +- GIOChannel *channel; +- NMSettingSerial *setting; +- +- if (!priv->monitor_iface) { +- nm_debug ("No monitoring udi provided"); +- return FALSE; +- } +- +- priv->monitor_device = g_object_new (NM_TYPE_SERIAL_DEVICE, +- NM_DEVICE_INTERFACE_UDI, nm_device_get_udi (NM_DEVICE (device)), +- NM_DEVICE_INTERFACE_IFACE, priv->monitor_iface, +- NULL); +- +- if (!priv->monitor_device) { +- nm_warning ("Creation of the monitoring device failed"); +- return FALSE; +- } +- +- setting = NM_SETTING_SERIAL (nm_setting_serial_new ()); +- if (!nm_serial_device_open (priv->monitor_device, setting)) { +- nm_warning ("Monitoring device open failed"); +- g_object_unref (setting); +- g_object_unref (priv->monitor_device); +- return FALSE; +- } +- +- g_object_unref (setting); +- +- channel = nm_serial_device_get_io_channel (priv->monitor_device); +- g_io_add_watch (channel, G_IO_IN | G_IO_ERR | G_IO_HUP, +- monitor_device_got_data, device); +- +- g_io_channel_unref (channel); +- +- return TRUE; +-} +- +-/*****************************************************************************/ +- +-static void +-nm_cdma_device_init (NMCdmaDevice *self) +-{ +- nm_device_set_device_type (NM_DEVICE (self), NM_DEVICE_TYPE_CDMA); +-} +- +-static gboolean +-unavailable_to_disconnected (gpointer user_data) +-{ +- nm_device_state_changed (NM_DEVICE (user_data), +- NM_DEVICE_STATE_DISCONNECTED, +- NM_DEVICE_STATE_REASON_NONE); +- return FALSE; +-} +- +-static void +-device_state_changed (NMDeviceInterface *device, +- NMDeviceState new_state, +- NMDeviceState old_state, +- NMDeviceStateReason reason, +- gpointer user_data) +-{ +- NMCdmaDevice *self = NM_CDMA_DEVICE (user_data); +- NMCdmaDevicePrivate *priv = NM_CDMA_DEVICE_GET_PRIVATE (self); +- +- /* Remove any previous delayed transition to disconnected */ +- if (priv->state_to_disconnected_id) { +- g_source_remove (priv->state_to_disconnected_id); +- priv->state_to_disconnected_id = 0; +- } +- +- /* If transitioning to UNAVAILBLE and we have a carrier, transition to +- * DISCONNECTED because the device is ready to use. Otherwise the carrier-on +- * handler will handle the transition to DISCONNECTED when the carrier is detected. +- */ +- if (new_state == NM_DEVICE_STATE_UNAVAILABLE) +- priv->state_to_disconnected_id = g_idle_add (unavailable_to_disconnected, self); +- +- /* Make sure we don't leave the serial device open */ +- switch (new_state) { +- case NM_DEVICE_STATE_UNMANAGED: +- case NM_DEVICE_STATE_UNAVAILABLE: +- case NM_DEVICE_STATE_FAILED: +- case NM_DEVICE_STATE_DISCONNECTED: +- nm_serial_device_close (NM_SERIAL_DEVICE (self)); +- break; +- default: +- break; +- } +-} +- +-static GObject* +-constructor (GType type, +- guint n_construct_params, +- GObjectConstructParam *construct_params) +-{ +- GObject *object; +- +- object = G_OBJECT_CLASS (nm_cdma_device_parent_class)->constructor (type, +- n_construct_params, +- construct_params); +- if (!object) +- return NULL; +- +- /* FIXME: Make the monitor device not required for now */ +- setup_monitor_device (NM_CDMA_DEVICE (object)); +-#if 0 +- if (!setup_monitor_device (NM_CDMA_DEVICE (object))) { +- g_object_unref (object); +- object = NULL; +- } +-#endif +- +- g_signal_connect (NM_DEVICE (object), "state-changed", +- G_CALLBACK (device_state_changed), NM_CDMA_DEVICE (object)); +- +- return object; +-} +- +-static void +-set_property (GObject *object, guint prop_id, +- const GValue *value, GParamSpec *pspec) +-{ +- NMCdmaDevicePrivate *priv = NM_CDMA_DEVICE_GET_PRIVATE (object); +- +- switch (prop_id) { +- case PROP_MONITOR_IFACE: +- /* Construct only */ +- priv->monitor_iface = g_value_dup_string (value); +- break; +- default: +- G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); +- break; +- } +-} +- +-static void +-get_property (GObject *object, guint prop_id, +- GValue *value, GParamSpec *pspec) +-{ +- NMCdmaDevicePrivate *priv = NM_CDMA_DEVICE_GET_PRIVATE (object); +- +- switch (prop_id) { +- case PROP_MONITOR_IFACE: +- g_value_set_string (value, priv->monitor_iface); +- break; +- default: +- G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); +- break; +- } +-} +- +-static void +-finalize (GObject *object) +-{ +- NMCdmaDevicePrivate *priv = NM_CDMA_DEVICE_GET_PRIVATE (object); +- +- if (priv->monitor_device) +- g_object_unref (priv->monitor_device); +- +- g_free (priv->monitor_iface); +- +- if (priv->state_to_disconnected_id) { +- g_source_remove (priv->state_to_disconnected_id); +- priv->state_to_disconnected_id = 0; +- } +- +- G_OBJECT_CLASS (nm_cdma_device_parent_class)->finalize (object); +-} +- +-static void +-nm_cdma_device_class_init (NMCdmaDeviceClass *klass) +-{ +- GObjectClass *object_class = G_OBJECT_CLASS (klass); +- NMDeviceClass *device_class = NM_DEVICE_CLASS (klass); +- NMSerialDeviceClass *serial_class = NM_SERIAL_DEVICE_CLASS (klass); +- +- g_type_class_add_private (object_class, sizeof (NMCdmaDevicePrivate)); +- +- object_class->constructor = constructor; +- object_class->get_property = get_property; +- object_class->set_property = set_property; +- object_class->finalize = finalize; +- +- device_class->get_best_auto_connection = real_get_best_auto_connection; +- device_class->get_generic_capabilities = real_get_generic_capabilities; +- device_class->act_stage1_prepare = real_act_stage1_prepare; +- device_class->connection_secrets_updated = real_connection_secrets_updated; +- +- serial_class->get_ppp_name = real_get_ppp_name; +- +- /* Properties */ +- g_object_class_install_property +- (object_class, PROP_MONITOR_IFACE, +- g_param_spec_string (NM_CDMA_DEVICE_MONITOR_IFACE, +- "Monitoring interface", +- "Monitoring interface", +- NULL, +- G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | NM_PROPERTY_PARAM_NO_EXPORT)); +- +- /* Signals */ +- signals[PROPERTIES_CHANGED] = +- nm_properties_changed_signal_new (object_class, +- G_STRUCT_OFFSET (NMCdmaDeviceClass, properties_changed)); +- +- dbus_g_object_type_install_info (G_TYPE_FROM_CLASS (klass), +- &dbus_glib_nm_cdma_device_object_info); +-} +diff --git a/src/nm-cdma-device.h b/src/nm-cdma-device.h +deleted file mode 100644 +index 50c44a3..0000000 +--- a/src/nm-cdma-device.h ++++ /dev/null +@@ -1,59 +0,0 @@ +-/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +-/* NetworkManager -- Network link manager +- * +- * 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. +- * +- * 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., +- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +- * +- * Copyright (C) 2008 Red Hat, Inc. +- * Copyright (C) 2008 Novell, Inc. +- */ +- +-#ifndef NM_CDMA_DEVICE_H +-#define NM_CDMA_DEVICE_H +- +-#include <nm-serial-device.h> +- +-G_BEGIN_DECLS +- +-#define NM_TYPE_CDMA_DEVICE (nm_cdma_device_get_type ()) +-#define NM_CDMA_DEVICE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_CDMA_DEVICE, NMCdmaDevice)) +-#define NM_CDMA_DEVICE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_CDMA_DEVICE, NMCdmaDeviceClass)) +-#define NM_IS_CDMA_DEVICE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_CDMA_DEVICE)) +-#define NM_IS_CDMA_DEVICE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_CDMA_DEVICE)) +-#define NM_CDMA_DEVICE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_CDMA_DEVICE, NMCdmaDeviceClass)) +- +-#define NM_CDMA_DEVICE_MONITOR_IFACE "monitor-iface" +- +-typedef struct { +- NMSerialDevice parent; +-} NMCdmaDevice; +- +-typedef struct { +- NMSerialDeviceClass parent; +- +- /* Signals */ +- void (*properties_changed) (NMCdmaDevice *device, GHashTable *properties); +-} NMCdmaDeviceClass; +- +-GType nm_cdma_device_get_type (void); +- +-NMCdmaDevice *nm_cdma_device_new (const char *udi, +- const char *data_iface, +- const char *monitor_iface, +- const char *driver, +- gboolean managed); +- +-G_END_DECLS +- +-#endif /* NM_CDMA_DEVICE_H */ +diff --git a/src/nm-hal-manager.c b/src/nm-hal-manager.c +index 2a97e59..b1f97ff 100644 +--- a/src/nm-hal-manager.c ++++ b/src/nm-hal-manager.c +@@ -32,9 +32,6 @@ + #include "nm-utils.h" + #include "nm-device-wifi.h" + #include "nm-device-ethernet.h" +-#include "nm-gsm-device.h" +-#include "nm-hso-gsm-device.h" +-#include "nm-cdma-device.h" + + /* Killswitch poll frequency in seconds */ + #define RFKILL_POLL_FREQUENCY 6 +@@ -219,145 +216,6 @@ wireless_device_creator (NMHalManager *self, const char *udi, gboolean managed) + return device; + } + +-/* Modem device creator */ +- +-static gboolean +-is_modem_device (NMHalManager *self, const char *udi) +-{ +- NMHalManagerPrivate *priv = NM_HAL_MANAGER_GET_PRIVATE (self); +- gboolean is_modem = FALSE; +- +- if (libhal_device_property_exists (priv->hal_ctx, udi, "info.category", NULL)) { +- char *category; +- +- category = libhal_device_get_property_string (priv->hal_ctx, udi, "info.category", NULL); +- if (category) { +- is_modem = strcmp (category, "serial") == 0; +- libhal_free_string (category); +- } +- } +- +- return is_modem; +-} +- +-static char * +-get_hso_netdev (LibHalContext *ctx, const char *udi) +-{ +- char *serial_parent, *netdev = NULL; +- char **netdevs; +- int num, i; +- +- /* Get the serial interface's originating device UDI, used to find the +- * originating device's netdev. +- */ +- serial_parent = libhal_device_get_property_string (ctx, udi, "serial.originating_device", NULL); +- if (!serial_parent) +- serial_parent = libhal_device_get_property_string (ctx, udi, "info.parent", NULL); +- if (!serial_parent) +- return NULL; +- +- /* Look for the originating device's netdev */ +- netdevs = libhal_find_device_by_capability (ctx, "net", &num, NULL); +- for (i = 0; netdevs && !netdev && (i < num); i++) { +- char *netdev_parent, *tmp; +- +- netdev_parent = libhal_device_get_property_string (ctx, netdevs[i], "net.originating_device", NULL); +- if (!netdev_parent) +- netdev_parent = libhal_device_get_property_string (ctx, netdevs[i], "net.physical_device", NULL); +- if (!netdev_parent) +- continue; +- +- if (!strcmp (netdev_parent, serial_parent)) { +- /* We found it */ +- tmp = libhal_device_get_property_string (ctx, netdevs[i], "net.interface", NULL); +- if (tmp) { +- netdev = g_strdup (tmp); +- libhal_free_string (tmp); +- } +- } +- +- libhal_free_string (netdev_parent); +- } +- libhal_free_string_array (netdevs); +- libhal_free_string (serial_parent); +- +- return netdev; +-} +- +-static GObject * +-modem_device_creator (NMHalManager *self, const char *udi, gboolean managed) +-{ +- NMHalManagerPrivate *priv = NM_HAL_MANAGER_GET_PRIVATE (self); +- char *serial_device; +- char *parent_udi; +- char *driver_name = NULL; +- GObject *device = NULL; +- char **capabilities, **iter; +- gboolean type_gsm = FALSE; +- gboolean type_cdma = FALSE; +- char *netdev = NULL; +- +- serial_device = libhal_device_get_property_string (priv->hal_ctx, udi, "serial.device", NULL); +- +- /* Get the driver */ +- parent_udi = libhal_device_get_property_string (priv->hal_ctx, udi, "info.parent", NULL); +- if (parent_udi) { +- driver_name = libhal_device_get_property_string (priv->hal_ctx, parent_udi, "info.linux.driver", NULL); +- libhal_free_string (parent_udi); +- } +- +- if (!serial_device || !driver_name) +- goto out; +- +- capabilities = libhal_device_get_property_strlist (priv->hal_ctx, udi, "modem.command_sets", NULL); +- /* 'capabilites' may be NULL */ +- for (iter = capabilities; iter && *iter; iter++) { +- if (!strcmp (*iter, "GSM-07.07")) { +- type_gsm = TRUE; +- break; +- } +- if (!strcmp (*iter, "IS-707-A")) { +- type_cdma = TRUE; +- break; +- } +- } +- g_strfreev (capabilities); +- +- /* Compatiblity with the pre-specification bits */ +- if (!type_gsm && !type_cdma) { +- capabilities = libhal_device_get_property_strlist (priv->hal_ctx, udi, "info.capabilities", NULL); +- for (iter = capabilities; *iter; iter++) { +- if (!strcmp (*iter, "gsm")) { +- type_gsm = TRUE; +- break; +- } +- if (!strcmp (*iter, "cdma")) { +- type_cdma = TRUE; +- break; +- } +- } +- g_strfreev (capabilities); +- } +- +- /* Special handling of 'hso' cards (until punted out to a modem manager) */ +- if (type_gsm && !strcmp (driver_name, "hso")) +- netdev = get_hso_netdev (priv->hal_ctx, udi); +- +- if (type_gsm) { +- if (netdev) +- device = (GObject *) nm_hso_gsm_device_new (udi, serial_device + strlen ("/dev/"), NULL, netdev, driver_name, managed); +- else +- device = (GObject *) nm_gsm_device_new (udi, serial_device + strlen ("/dev/"), NULL, driver_name, managed); +- } else if (type_cdma) +- device = (GObject *) nm_cdma_device_new (udi, serial_device + strlen ("/dev/"), NULL, driver_name, managed); +- +-out: +- libhal_free_string (serial_device); +- libhal_free_string (driver_name); +- +- return device; +-} +- + static void + register_built_in_creators (NMHalManager *self) + { +@@ -379,14 +237,6 @@ register_built_in_creators (NMHalManager *self) + creator->is_device_fn = is_wireless_device; + creator->creator_fn = wireless_device_creator; + priv->device_creators = g_slist_append (priv->device_creators, creator); +- +- /* Modem */ +- creator = g_slice_new0 (DeviceCreator); +- creator->device_type_name = g_strdup ("Modem"); +- creator->capability_str = g_strdup ("modem"); +- creator->is_device_fn = is_modem_device; +- creator->creator_fn = modem_device_creator; +- priv->device_creators = g_slist_append (priv->device_creators, creator); + } + + static void +diff --git a/src/nm-hso-gsm-device.c b/src/nm-hso-gsm-device.c +deleted file mode 100644 +index 15032a3..0000000 +--- a/src/nm-hso-gsm-device.c ++++ /dev/null +@@ -1,593 +0,0 @@ +-/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +-/* NetworkManager -- Network link manager +- * +- * 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. +- * +- * 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., +- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +- * +- * Copyright (C) 2008 Red Hat, Inc. +- */ +- +-#include <stdio.h> +-#include <string.h> +-#include <errno.h> +-#include <stdlib.h> +-#include <arpa/inet.h> +-#include <dbus/dbus-glib.h> +- +-#include "nm-device.h" +-#include "nm-hso-gsm-device.h" +-#include "nm-gsm-device.h" +-#include "nm-device-interface.h" +-#include "nm-device-private.h" +-#include "nm-setting-gsm.h" +-#include "nm-utils.h" +-#include "nm-properties-changed-signal.h" +-#include "nm-setting-connection.h" +-#include "NetworkManagerSystem.h" +- +-G_DEFINE_TYPE (NMHsoGsmDevice, nm_hso_gsm_device, NM_TYPE_GSM_DEVICE) +- +-#define NM_HSO_GSM_DEVICE_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_HSO_GSM_DEVICE, NMHsoGsmDevicePrivate)) +- +-extern const DBusGObjectInfo dbus_glib_nm_gsm_device_object_info; +- +-#define GSM_CID "gsm-cid" +-#define HSO_SECRETS_TRIES "gsm-secrets-tries" +- +-typedef struct { +- char *netdev_iface; +- NMIP4Config *pending_ip4_config; +-} NMHsoGsmDevicePrivate; +- +-enum { +- PROP_0, +- PROP_NETDEV_IFACE, +- +- LAST_PROP +-}; +- +-NMHsoGsmDevice * +-nm_hso_gsm_device_new (const char *udi, +- const char *data_iface, +- const char *monitor_iface, +- const char *netdev_iface, +- const char *driver, +- gboolean managed) +-{ +- g_return_val_if_fail (udi != NULL, NULL); +- g_return_val_if_fail (data_iface != NULL, NULL); +- g_return_val_if_fail (driver != NULL, NULL); +- g_return_val_if_fail (netdev_iface != NULL, NULL); +- +- return (NMHsoGsmDevice *) g_object_new (NM_TYPE_HSO_GSM_DEVICE, +- NM_DEVICE_INTERFACE_UDI, udi, +- NM_DEVICE_INTERFACE_IFACE, data_iface, +- NM_DEVICE_INTERFACE_DRIVER, driver, +- NM_GSM_DEVICE_MONITOR_IFACE, monitor_iface, +- NM_HSO_GSM_DEVICE_NETDEV_IFACE, netdev_iface, +- NM_DEVICE_INTERFACE_MANAGED, managed, +- NULL); +-} +- +-static void +-modem_wait_for_reply (NMGsmDevice *self, +- const char *command, +- guint timeout, +- const char **responses, +- const char **terminators, +- NMSerialWaitForReplyFn callback, +- gpointer user_data) +-{ +- NMSerialDevice *serial = NM_SERIAL_DEVICE (self); +- guint id = 0; +- +- if (nm_serial_device_send_command_string (serial, command)) +- id = nm_serial_device_wait_for_reply (serial, timeout, responses, terminators, callback, user_data); +- +- if (id == 0) +- nm_device_state_changed (NM_DEVICE (self), NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_UNKNOWN); +-} +- +-static NMSetting * +-gsm_device_get_setting (NMGsmDevice *device, GType setting_type) +-{ +- NMActRequest *req; +- NMSetting *setting = NULL; +- +- req = nm_device_get_act_request (NM_DEVICE (device)); +- if (req) { +- NMConnection *connection; +- +- connection = nm_act_request_get_connection (req); +- if (connection) +- setting = nm_connection_get_setting (connection, setting_type); +- } +- +- return setting; +-} +- +-static void +-hso_call_done (NMSerialDevice *device, +- int reply_index, +- const char *reply, +- gpointer user_data) +-{ +- gboolean success = FALSE; +- +- switch (reply_index) { +- case 0: +- nm_info ("Connected, Woo!"); +- success = TRUE; +- break; +- default: +- nm_warning ("Connect request failed"); +- break; +- } +- +- if (success) +- nm_device_activate_schedule_stage3_ip_config_start (NM_DEVICE (device)); +- else +- nm_device_state_changed (NM_DEVICE (device), NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_MODEM_DIAL_FAILED); +-} +- +-static void +-hso_clear_done (NMSerialDevice *device, +- int reply_index, +- const char *reply, +- gpointer user_data) +-{ +- const char *responses[] = { "_OWANCALL: ", "ERROR", NULL }; +- guint cid = GPOINTER_TO_UINT (user_data); +- char *command; +- +- /* Try to connect */ +- command = g_strdup_printf ("AT_OWANCALL=%d,1,1", cid); +- modem_wait_for_reply (NM_GSM_DEVICE (device), command, 10, responses, responses, hso_call_done, NULL); +- g_free (command); +-} +- +-static void +-hso_auth_done (NMSerialDevice *device, +- int reply_index, +- const char *reply, +- gpointer user_data) +-{ +- gboolean success = FALSE; +- const char *responses[] = { "_OWANCALL: ", "ERROR", "NO CARRIER", NULL }; +- guint cid = GPOINTER_TO_UINT (user_data); +- char *command; +- +- switch (reply_index) { +- case 0: +- nm_info ("Authentication successful!"); +- success = TRUE; +- break; +- default: +- nm_warning ("Authentication failed"); +- break; +- } +- +- if (!success) { +- nm_device_state_changed (NM_DEVICE (device), NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_MODEM_DIAL_FAILED); +- return; +- } +- +- /* Kill any existing connection */ +- command = g_strdup_printf ("AT_OWANCALL=%d,0,1", cid); +- modem_wait_for_reply (NM_GSM_DEVICE (device), command, 5, responses, responses, hso_clear_done, GUINT_TO_POINTER (cid)); +- g_free (command); +-} +- +-static void +-do_hso_auth (NMHsoGsmDevice *device) +-{ +- NMSettingGsm *s_gsm; +- NMActRequest *req; +- const char *responses[] = { "OK", "ERROR", "ERR", NULL }; +- char *command; +- const char *gsm_username; +- const char *gsm_password; +- guint cid; +- +- req = nm_device_get_act_request (NM_DEVICE (device)); +- g_assert (req); +- +- cid = GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (req), GSM_CID)); +- +- s_gsm = NM_SETTING_GSM (gsm_device_get_setting (NM_GSM_DEVICE (device), NM_TYPE_SETTING_GSM)); +- +- gsm_username = nm_setting_gsm_get_username (s_gsm); +- gsm_password = nm_setting_gsm_get_password (s_gsm); +- +- command = g_strdup_printf ("AT$QCPDPP=%d,1,\"%s\",\"%s\"", +- cid, +- gsm_password ? gsm_password : "", +- gsm_username ? gsm_username : ""); +- modem_wait_for_reply (NM_GSM_DEVICE (device), command, 5, responses, responses, hso_auth_done, GUINT_TO_POINTER (cid)); +- g_free (command); +-} +- +-static NMActStageReturn +-real_act_stage2_config (NMDevice *device, NMDeviceStateReason *reason) +-{ +- NMActRequest *req; +- NMConnection *connection; +- const char *setting_name; +- GPtrArray *hints = NULL; +- const char *hint1 = NULL, *hint2 = NULL; +- guint32 tries; +- +- req = nm_device_get_act_request (device); +- g_assert (req); +- connection = nm_act_request_get_connection (req); +- g_assert (connection); +- +- setting_name = nm_connection_need_secrets (connection, &hints); +- if (!setting_name) { +- do_hso_auth (NM_HSO_GSM_DEVICE (device)); +- return NM_ACT_STAGE_RETURN_POSTPONE; +- } +- +- if (hints) { +- if (hints->len > 0) +- hint1 = g_ptr_array_index (hints, 0); +- if (hints->len > 1) +- hint2 = g_ptr_array_index (hints, 1); +- } +- +- nm_device_state_changed (device, NM_DEVICE_STATE_NEED_AUTH, NM_DEVICE_STATE_REASON_NONE); +- +- tries = GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (connection), HSO_SECRETS_TRIES)); +- nm_act_request_request_connection_secrets (req, +- setting_name, +- tries ? TRUE : FALSE, +- SECRETS_CALLER_HSO_GSM, +- hint1, +- hint2); +- g_object_set_data (G_OBJECT (connection), HSO_SECRETS_TRIES, GUINT_TO_POINTER (++tries)); +- +- if (hints) +- g_ptr_array_free (hints, TRUE); +- +- return NM_ACT_STAGE_RETURN_POSTPONE; +-} +- +-static void +-real_do_dial (NMGsmDevice *device, guint cid) +-{ +- NMActRequest *req; +- +- req = nm_device_get_act_request (NM_DEVICE (device)); +- g_assert (req); +- g_object_set_data (G_OBJECT (req), GSM_CID, GUINT_TO_POINTER (cid)); +- +- nm_device_activate_schedule_stage2_device_config (NM_DEVICE (device)); +-} +- +-#define OWANDATA_TAG "_OWANDATA: " +- +-static void +-hso_ip4_config_response (NMSerialDevice *device, +- int reply_index, +- const char *response, +- gpointer user_data) +-{ +- NMHsoGsmDevicePrivate *priv = NM_HSO_GSM_DEVICE_GET_PRIVATE (device); +- NMActRequest *req; +- char **items, **iter; +- guint cid, i; +- guint32 dns1 = 0, dns2 = 0, ip4_address = 0; +- +- if ( (reply_index < 0) +- || !response +- || strncmp (response, OWANDATA_TAG, strlen (OWANDATA_TAG))) { +- nm_device_activate_schedule_stage4_ip_config_timeout (NM_DEVICE (device)); +- return; +- } +- +- req = nm_device_get_act_request (NM_DEVICE (device)); +- g_assert (req); +- cid = GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (req), GSM_CID)); +- +- items = g_strsplit (response + strlen (OWANDATA_TAG), ", ", 0); +- for (iter = items, i = 0; *iter; iter++, i++) { +- if (i == 0) { /* CID */ +- long int tmp; +- +- errno = 0; +- tmp = strtol (*iter, NULL, 10); +- if (errno != 0 || tmp < 0 || (guint) tmp != cid) { +- nm_warning ("%s: unknown CID in OWANDATA response (got %d, expected %d)", +- nm_device_get_iface (NM_DEVICE (device)), +- (guint) tmp, cid); +- goto out; +- } +- } else if (i == 1) { /* IP address */ +- if (inet_pton (AF_INET, *iter, &ip4_address) <= 0) +- ip4_address = 0; +- } else if (i == 3) { /* DNS 1 */ +- if (inet_pton (AF_INET, *iter, &dns1) <= 0) +- dns1 = 0; +- } else if (i == 4) { /* DNS 2 */ +- if (inet_pton (AF_INET, *iter, &dns2) <= 0) +- dns2 = 0; +- } +- } +- +-out: +- g_strfreev (items); +- +- if (ip4_address) { +- NMIP4Address *addr; +- +- priv->pending_ip4_config = nm_ip4_config_new (); +- +- addr = nm_ip4_address_new (); +- nm_ip4_address_set_address (addr, ip4_address); +- nm_ip4_address_set_prefix (addr, 32); +- +- nm_ip4_config_take_address (priv->pending_ip4_config, addr); +- +- if (dns1) +- nm_ip4_config_add_nameserver (priv->pending_ip4_config, dns1); +- if (dns2) +- nm_ip4_config_add_nameserver (priv->pending_ip4_config, dns2); +- +- nm_device_activate_schedule_stage4_ip_config_get (NM_DEVICE (device)); +- } else { +- nm_device_state_changed (NM_DEVICE (device), +- NM_DEVICE_STATE_FAILED, +- NM_DEVICE_STATE_REASON_IP_CONFIG_UNAVAILABLE); +- } +-} +- +-static NMActStageReturn +-real_act_stage3_ip_config_start (NMDevice *device, NMDeviceStateReason *reason) +-{ +- NMActRequest *req; +- char *command; +- gint cid; +- const char *responses[] = { "_OWANDATA: ", NULL }; +- const char *terminators[] = { "OK", "ERROR", "ERR", NULL }; +- +- req = nm_device_get_act_request (device); +- g_assert (req); +- +- cid = GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (req), GSM_CID)); +- command = g_strdup_printf ("AT_OWANDATA=%d", cid); +- modem_wait_for_reply (NM_GSM_DEVICE (device), command, 5, responses, terminators, hso_ip4_config_response, NULL); +- g_free (command); +- +- return NM_ACT_STAGE_RETURN_POSTPONE; +-} +- +-static NMActStageReturn +-real_act_stage4_get_ip4_config (NMDevice *device, +- NMIP4Config **config, +- NMDeviceStateReason *reason) +-{ +- NMHsoGsmDevice *self = NM_HSO_GSM_DEVICE (device); +- NMHsoGsmDevicePrivate *priv = NM_HSO_GSM_DEVICE_GET_PRIVATE (self); +- gboolean no_firmware = FALSE; +- +- g_return_val_if_fail (config != NULL, NM_ACT_STAGE_RETURN_FAILURE); +- g_return_val_if_fail (*config == NULL, NM_ACT_STAGE_RETURN_FAILURE); +- +- nm_device_set_ip_iface (device, priv->netdev_iface); +- if (!nm_device_hw_bring_up (device, TRUE, &no_firmware)) { +- if (no_firmware) +- *reason = NM_DEVICE_STATE_REASON_FIRMWARE_MISSING; +- else +- *reason = NM_DEVICE_STATE_REASON_CONFIG_FAILED; +- return NM_ACT_STAGE_RETURN_FAILURE; +- } +- +- *config = priv->pending_ip4_config; +- priv->pending_ip4_config = NULL; +- return NM_ACT_STAGE_RETURN_SUCCESS; +-} +- +-static void +-real_connection_secrets_updated (NMDevice *device, +- NMConnection *connection, +- GSList *updated_settings, +- RequestSecretsCaller caller) +-{ +- g_return_if_fail (nm_device_get_state (device) == NM_DEVICE_STATE_NEED_AUTH); +- +- if (caller == SECRETS_CALLER_HSO_GSM) { /* HSO PPP auth */ +- nm_device_activate_schedule_stage2_device_config (device); +- return; +- } +- +- /* Let parent handle other auth like PIN/PUK */ +- NM_DEVICE_CLASS (nm_hso_gsm_device_parent_class)->connection_secrets_updated (device, connection, updated_settings, caller); +-} +- +-static void +-real_deactivate_quickly (NMDevice *device) +-{ +- NMHsoGsmDevicePrivate *priv = NM_HSO_GSM_DEVICE_GET_PRIVATE (device); +- NMActRequest *req; +- guint cid; +- char *command; +- +- if (priv->pending_ip4_config) { +- g_object_unref (priv->pending_ip4_config); +- priv->pending_ip4_config = NULL; +- } +- +- /* Don't leave the modem connected */ +- req = nm_device_get_act_request (device); +- if (req) { +- cid = GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (req), GSM_CID)); +- if (cid) { +- const char *responses[] = { "OK", "ERROR", "ERR", NULL }; +- int reply; +- +- /* Disconnect and disable asynchonous notification to keep serial +- * buffer empty after the OK. +- */ +- command = g_strdup_printf ("AT_OWANCALL=%d,0,0", cid); +- nm_serial_device_send_command_string (NM_SERIAL_DEVICE (device), command); +- reply = nm_serial_device_wait_reply_blocking (NM_SERIAL_DEVICE (device), 5, responses, responses); +- g_free (command); +- } +- } +- +- if (NM_DEVICE_CLASS (nm_hso_gsm_device_parent_class)->deactivate_quickly) +- NM_DEVICE_CLASS (nm_hso_gsm_device_parent_class)->deactivate_quickly (device); +-} +- +-static void +-real_deactivate (NMDevice *device) +-{ +- NMHsoGsmDevicePrivate *priv = NM_HSO_GSM_DEVICE_GET_PRIVATE (device); +- +- if (priv->netdev_iface) { +- nm_system_device_flush_ip4_routes_with_iface (priv->netdev_iface); +- nm_system_device_flush_ip4_addresses_with_iface (priv->netdev_iface); +- nm_system_device_set_up_down_with_iface (priv->netdev_iface, FALSE, NULL); +- } +- nm_device_set_ip_iface (device, NULL); +- +- if (NM_DEVICE_CLASS (nm_hso_gsm_device_parent_class)->deactivate) +- NM_DEVICE_CLASS (nm_hso_gsm_device_parent_class)->deactivate (device); +-} +- +-static gboolean +-real_hw_is_up (NMDevice *device) +-{ +- NMHsoGsmDevicePrivate *priv = NM_HSO_GSM_DEVICE_GET_PRIVATE (device); +- NMDeviceState state; +- +- state = nm_device_interface_get_state (NM_DEVICE_INTERFACE (device)); +- +- if ( priv->pending_ip4_config +- || (state == NM_DEVICE_STATE_IP_CONFIG) +- || (state == NM_DEVICE_STATE_ACTIVATED)) +- return nm_system_device_is_up_with_iface (priv->netdev_iface); +- +- return TRUE; +-} +- +-static gboolean +-real_hw_bring_up (NMDevice *device, gboolean *no_firmware) +-{ +- NMHsoGsmDevicePrivate *priv = NM_HSO_GSM_DEVICE_GET_PRIVATE (device); +- NMDeviceState state; +- +- state = nm_device_interface_get_state (NM_DEVICE_INTERFACE (device)); +- +- if ( priv->pending_ip4_config +- || (state == NM_DEVICE_STATE_IP_CONFIG) +- || (state == NM_DEVICE_STATE_ACTIVATED)) +- return nm_system_device_set_up_down_with_iface (priv->netdev_iface, TRUE, no_firmware); +- +- return TRUE; +-} +- +-static void +-nm_hso_gsm_device_init (NMHsoGsmDevice *self) +-{ +-} +- +-static GObject* +-constructor (GType type, +- guint n_params, +- GObjectConstructParam *params) +-{ +- return G_OBJECT_CLASS (nm_hso_gsm_device_parent_class)->constructor (type, n_params, params); +-} +- +-static void +-set_property (GObject *object, guint prop_id, +- const GValue *value, GParamSpec *pspec) +-{ +- NMHsoGsmDevicePrivate *priv = NM_HSO_GSM_DEVICE_GET_PRIVATE (object); +- +- switch (prop_id) { +- case PROP_NETDEV_IFACE: +- /* Construct only */ +- priv->netdev_iface = g_value_dup_string (value); +- break; +- default: +- G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); +- break; +- } +-} +- +-static void +-get_property (GObject *object, guint prop_id, +- GValue *value, GParamSpec *pspec) +-{ +- NMHsoGsmDevicePrivate *priv = NM_HSO_GSM_DEVICE_GET_PRIVATE (object); +- +- switch (prop_id) { +- case PROP_NETDEV_IFACE: +- g_value_set_string (value, priv->netdev_iface); +- break; +- default: +- G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); +- break; +- } +-} +- +-static void +-finalize (GObject *object) +-{ +- NMHsoGsmDevicePrivate *priv = NM_HSO_GSM_DEVICE_GET_PRIVATE (object); +- +- g_free (priv->netdev_iface); +- +- G_OBJECT_CLASS (nm_hso_gsm_device_parent_class)->finalize (object); +-} +- +-static void +-nm_hso_gsm_device_class_init (NMHsoGsmDeviceClass *klass) +-{ +- GObjectClass *object_class = G_OBJECT_CLASS (klass); +- NMDeviceClass *device_class = NM_DEVICE_CLASS (klass); +- NMGsmDeviceClass *gsm_class = NM_GSM_DEVICE_CLASS (klass); +- +- g_type_class_add_private (object_class, sizeof (NMHsoGsmDevicePrivate)); +- +- object_class->constructor = constructor; +- object_class->get_property = get_property; +- object_class->set_property = set_property; +- object_class->finalize = finalize; +- +- device_class->act_stage2_config = real_act_stage2_config; +- device_class->act_stage3_ip_config_start = real_act_stage3_ip_config_start; +- device_class->act_stage4_get_ip4_config = real_act_stage4_get_ip4_config; +- device_class->connection_secrets_updated = real_connection_secrets_updated; +- device_class->deactivate_quickly = real_deactivate_quickly; +- device_class->deactivate = real_deactivate; +- device_class->hw_is_up = real_hw_is_up; +- device_class->hw_bring_up = real_hw_bring_up; +- +- gsm_class->do_dial = real_do_dial; +- +- /* Properties */ +- g_object_class_install_property +- (object_class, PROP_NETDEV_IFACE, +- g_param_spec_string (NM_HSO_GSM_DEVICE_NETDEV_IFACE, +- "Network interface", +- "Network interface", +- NULL, +- G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | NM_PROPERTY_PARAM_NO_EXPORT)); +- +- dbus_g_object_type_install_info (G_TYPE_FROM_CLASS (klass), +- &dbus_glib_nm_gsm_device_object_info); +-} +diff --git a/src/nm-hso-gsm-device.h b/src/nm-hso-gsm-device.h +deleted file mode 100644 +index 5b6b48a..0000000 +--- a/src/nm-hso-gsm-device.h ++++ /dev/null +@@ -1,56 +0,0 @@ +-/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +-/* NetworkManager -- Network link manager +- * +- * 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. +- * +- * 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., +- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +- * +- * Copyright (C) 2008 Red Hat, Inc. +- */ +- +-#ifndef NM_HSO_GSM_DEVICE_H +-#define NM_HSO_GSM_DEVICE_H +- +-#include <nm-gsm-device.h> +- +-G_BEGIN_DECLS +- +-#define NM_TYPE_HSO_GSM_DEVICE (nm_hso_gsm_device_get_type ()) +-#define NM_HSO_GSM_DEVICE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_HSO_GSM_DEVICE, NMHsoGsmDevice)) +-#define NM_HSO_GSM_DEVICE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_HSO_GSM_DEVICE, NMHsoGsmDeviceClass)) +-#define NM_IS_HSO_GSM_DEVICE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_HSO_GSM_DEVICE)) +-#define NM_IS_HSO_GSM_DEVICE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_HSO_GSM_DEVICE)) +-#define NM_HSO_GSM_DEVICE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_HSO_GSM_DEVICE, NMHsoGsmDeviceClass)) +- +-#define NM_HSO_GSM_DEVICE_NETDEV_IFACE "netdev-iface" +- +-typedef struct { +- NMGsmDevice parent; +-} NMHsoGsmDevice; +- +-typedef struct { +- NMGsmDeviceClass parent; +-} NMHsoGsmDeviceClass; +- +-GType nm_hso_gsm_device_get_type (void); +- +-NMHsoGsmDevice *nm_hso_gsm_device_new (const char *udi, +- const char *data_iface, +- const char *monitor_iface, +- const char *netdev_iface, +- const char *driver, +- gboolean managed); +- +-G_END_DECLS +- +-#endif /* NM_HSO_GSM_DEVICE_H */ +diff --git a/src/nm-manager.c b/src/nm-manager.c +index e5ebdb2..d323ceb 100644 +--- a/src/nm-manager.c ++++ b/src/nm-manager.c +@@ -28,6 +28,7 @@ + #include "nm-utils.h" + #include "nm-dbus-manager.h" + #include "nm-vpn-manager.h" ++#include "nm-modem-manager.h" + #include "nm-device-interface.h" + #include "nm-device-private.h" + #include "nm-device-wifi.h" +@@ -95,6 +96,10 @@ static void system_settings_properties_changed_cb (DBusGProxy *proxy, + GHashTable *properties, + gpointer user_data); + ++static void add_device (NMManager *self, NMDevice *device, const char *type_name); ++static void remove_one_device (NMManager *manager, NMDevice *device); ++ ++ + #define SSD_POKE_INTERVAL 120000 + + typedef struct { +@@ -133,6 +138,10 @@ typedef struct { + NMVPNManager *vpn_manager; + guint vpn_manager_id; + ++ NMModemManager *modem_manager; ++ guint modem_added_id; ++ guint modem_removed_id; ++ + DBusGProxy *aipd_proxy; + + gboolean disposed; +@@ -241,6 +250,36 @@ vpn_manager_connection_deactivated_cb (NMVPNManager *manager, + } + + static void ++modem_added (NMModemManager *modem_manager, ++ NMDevice *modem, ++ gpointer user_data) ++{ ++ NMDeviceType type; ++ const char *type_name; ++ ++ type = nm_device_get_device_type (NM_DEVICE (modem)); ++ if (type == NM_DEVICE_TYPE_GSM) ++ type_name = "GSM modem"; ++ else if (type == NM_DEVICE_TYPE_CDMA) ++ type_name = "CDMA modem"; ++ else ++ type_name = "Unknown modem"; ++ ++ add_device (NM_MANAGER (user_data), NM_DEVICE (g_object_ref (modem)), type_name); ++} ++ ++static void ++modem_removed (NMModemManager *modem_manager, ++ NMDevice *modem, ++ gpointer user_data) ++{ ++ NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (user_data); ++ ++ remove_one_device (NM_MANAGER (user_data), modem); ++ priv->devices = g_slist_remove (priv->devices, modem); ++} ++ ++static void + aipd_handle_event (DBusGProxy *proxy, + const char *event, + const char *iface, +@@ -303,6 +342,12 @@ nm_manager_init (NMManager *manager) + g_free, + g_object_unref); + ++ priv->modem_manager = nm_modem_manager_get (); ++ priv->modem_added_id = g_signal_connect (priv->modem_manager, "device-added", ++ G_CALLBACK (modem_added), manager); ++ priv->modem_removed_id = g_signal_connect (priv->modem_manager, "device-removed", ++ G_CALLBACK (modem_removed), manager); ++ + priv->vpn_manager = nm_vpn_manager_get (); + id = g_signal_connect (G_OBJECT (priv->vpn_manager), "connection-deactivated", + G_CALLBACK (vpn_manager_connection_deactivated_cb), manager); +@@ -502,6 +547,16 @@ dispose (GObject *object) + } + g_object_unref (priv->vpn_manager); + ++ if (priv->modem_added_id) { ++ g_source_remove (priv->modem_added_id); ++ priv->modem_added_id = 0; ++ } ++ if (priv->modem_removed_id) { ++ g_source_remove (priv->modem_removed_id); ++ priv->modem_removed_id = 0; ++ } ++ g_object_unref (priv->modem_manager); ++ + g_object_unref (priv->dbus_mgr); + g_object_unref (priv->hal_mgr); + +@@ -1644,58 +1699,66 @@ next: + } + + static void +-hal_manager_udi_added_cb (NMHalManager *hal_mgr, +- const char *udi, +- const char *type_name, +- NMDeviceCreatorFn creator_fn, +- gpointer user_data) ++add_device (NMManager *self, NMDevice *device, const char *type_name) + { +- NMManager *self = NM_MANAGER (user_data); + NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); +- GObject *device; + const char *iface; + +- if (priv->sleeping) +- return; +- +- /* Make sure the device is not already in the device list */ +- if (nm_manager_get_device_by_udi (self, udi)) +- return; +- +- device = creator_fn (hal_mgr, udi, nm_manager_udi_is_managed (self, udi)); +- if (!device) +- return; +- + priv->devices = g_slist_append (priv->devices, device); + + g_signal_connect (device, "state-changed", +- G_CALLBACK (manager_device_state_changed), +- self); ++ G_CALLBACK (manager_device_state_changed), ++ self); + + /* Attach to the access-point-added signal so that the manager can fill + * non-SSID-broadcasting APs with an SSID. + */ + if (NM_IS_DEVICE_WIFI (device)) { + g_signal_connect (device, "hidden-ap-found", +- G_CALLBACK (manager_hidden_ap_found), +- self); ++ G_CALLBACK (manager_hidden_ap_found), ++ self); + + /* Set initial rfkill state */ + nm_device_wifi_set_enabled (NM_DEVICE_WIFI (device), priv->wireless_enabled); + } + +- iface = nm_device_get_iface (NM_DEVICE (device)); ++ iface = nm_device_get_iface (device); + nm_info ("Found new %s device '%s'.", type_name, iface); + + dbus_g_connection_register_g_object (nm_dbus_manager_get_connection (priv->dbus_mgr), + nm_device_get_udi (NM_DEVICE (device)), +- device); +- nm_info ("(%s): exported as %s", iface, udi); ++ G_OBJECT (device)); ++ nm_info ("(%s): exported as %s", iface, nm_device_get_udi (device)); + + g_signal_emit (self, signals[DEVICE_ADDED], 0, device); + } + + static void ++hal_manager_udi_added_cb (NMHalManager *hal_mgr, ++ const char *udi, ++ const char *type_name, ++ NMDeviceCreatorFn creator_fn, ++ gpointer user_data) ++{ ++ NMManager *self = NM_MANAGER (user_data); ++ NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); ++ GObject *device; ++ ++ if (priv->sleeping) ++ return; ++ ++ /* Make sure the device is not already in the device list */ ++ if (nm_manager_get_device_by_udi (self, udi)) ++ return; ++ ++ device = creator_fn (hal_mgr, udi, nm_manager_udi_is_managed (self, udi)); ++ if (!device) ++ return; ++ ++ add_device (self, NM_DEVICE (device), type_name); ++} ++ ++static void + hal_manager_udi_removed_cb (NMHalManager *manager, + const char *udi, + gpointer user_data) +diff --git a/src/nm-serial-device.c b/src/nm-serial-device.c +deleted file mode 100644 +index 184c67c..0000000 +--- a/src/nm-serial-device.c ++++ /dev/null +@@ -1,1178 +0,0 @@ +-/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +-/* NetworkManager -- Network link manager +- * +- * 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. +- * +- * 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., +- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +- * +- * Copyright (C) 2007 - 2008 Novell, Inc. +- * Copyright (C) 2007 - 2008 Red Hat, Inc. +- */ +- +-#define _GNU_SOURCE /* for strcasestr() */ +- +-#include <termio.h> +-#include <unistd.h> +-#include <sys/types.h> +-#include <sys/stat.h> +-#include <fcntl.h> +-#include <errno.h> +-#include <sys/ioctl.h> +-#include <string.h> +-#include <stdlib.h> +-#include <glib.h> +- +-#include "nm-serial-device.h" +-#include "nm-device-interface.h" +-#include "nm-device-private.h" +-#include "ppp-manager/nm-ppp-manager.h" +-#include "nm-setting-ppp.h" +-#include "nm-marshal.h" +-#include "nm-utils.h" +-#include "nm-serial-device-glue.h" +-#include "NetworkManagerUtils.h" +- +-static gboolean serial_debug = FALSE; +- +-#define SERIAL_BUF_SIZE 2048 +- +-G_DEFINE_TYPE (NMSerialDevice, nm_serial_device, NM_TYPE_DEVICE) +- +-#define NM_SERIAL_DEVICE_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_SERIAL_DEVICE, NMSerialDevicePrivate)) +- +-typedef struct { +- int fd; +- GIOChannel *channel; +- NMPPPManager *ppp_manager; +- NMIP4Config *pending_ip4_config; +- struct termios old_t; +- +- guint pending_id; +- guint timeout_id; +- +- /* PPP stats */ +- guint32 in_bytes; +- guint32 out_bytes; +-} NMSerialDevicePrivate; +- +-enum { +- PPP_STATS, +- +- LAST_SIGNAL +-}; +- +-static guint signals[LAST_SIGNAL] = { 0 }; +- +-static int +-parse_baudrate (guint i) +-{ +- int speed; +- +- switch (i) { +- case 0: +- speed = B0; +- break; +- case 50: +- speed = B50; +- break; +- case 75: +- speed = B75; +- break; +- case 110: +- speed = B110; +- break; +- case 150: +- speed = B150; +- break; +- case 300: +- speed = B300; +- break; +- case 600: +- speed = B600; +- break; +- case 1200: +- speed = B1200; +- break; +- case 2400: +- speed = B2400; +- break; +- case 4800: +- speed = B4800; +- break; +- case 9600: +- speed = B9600; +- break; +- case 19200: +- speed = B19200; +- break; +- case 38400: +- speed = B38400; +- break; +- case 57600: +- speed = B57600; +- break; +- case 115200: +- speed = B115200; +- break; +- case 460800: +- speed = B460800; +- break; +- default: +- g_warning ("Invalid baudrate '%d'", i); +- speed = B9600; +- } +- +- return speed; +-} +- +-static int +-parse_bits (guint i) +-{ +- int bits; +- +- switch (i) { +- case 5: +- bits = CS5; +- break; +- case 6: +- bits = CS6; +- break; +- case 7: +- bits = CS7; +- break; +- case 8: +- bits = CS8; +- break; +- default: +- g_warning ("Invalid bits (%d). Valid values are 5, 6, 7, 8.", i); +- bits = CS8; +- } +- +- return bits; +-} +- +-static int +-parse_parity (char c) +-{ +- int parity; +- +- switch (c) { +- case 'n': +- case 'N': +- parity = 0; +- break; +- case 'e': +- case 'E': +- parity = PARENB; +- break; +- case 'o': +- case 'O': +- parity = PARENB | PARODD; +- break; +- default: +- g_warning ("Invalid parity (%c). Valid values are n, e, o", c); +- parity = 0; +- } +- +- return parity; +-} +- +-static int +-parse_stopbits (guint i) +-{ +- int stopbits; +- +- switch (i) { +- case 1: +- stopbits = 0; +- break; +- case 2: +- stopbits = CSTOPB; +- break; +- default: +- g_warning ("Invalid stop bits (%d). Valid values are 1 and 2)", i); +- stopbits = 0; +- } +- +- return stopbits; +-} +- +-static inline void +-nm_serial_debug (const char *prefix, const char *data, int len) +-{ +- GString *str; +- int i; +- +- if (!serial_debug) +- return; +- +- str = g_string_sized_new (len); +- for (i = 0; i < len; i++) { +- if (data[i] == '\0') +- g_string_append_c (str, ' '); +- else if (data[i] == '\r') +- g_string_append_c (str, '\n'); +- else +- g_string_append_c (str, data[i]); +- } +- +- nm_debug ("%s '%s'", prefix, str->str); +- g_string_free (str, TRUE); +-} +- +-static NMSetting * +-serial_device_get_setting (NMSerialDevice *device, GType setting_type) +-{ +- NMActRequest *req; +- NMSetting *setting = NULL; +- +- req = nm_device_get_act_request (NM_DEVICE (device)); +- if (req) { +- NMConnection *connection; +- +- connection = nm_act_request_get_connection (req); +- if (connection) +- setting = nm_connection_get_setting (connection, setting_type); +- } +- +- return setting; +-} +- +-/* Timeout handling */ +- +-static void +-nm_serial_device_timeout_removed (gpointer data) +-{ +- NMSerialDevicePrivate *priv = NM_SERIAL_DEVICE_GET_PRIVATE (data); +- +- priv->timeout_id = 0; +-} +- +-static gboolean +-nm_serial_device_timed_out (gpointer data) +-{ +- NMSerialDevicePrivate *priv = NM_SERIAL_DEVICE_GET_PRIVATE (data); +- +- /* Cancel data reading */ +- if (priv->pending_id) +- g_source_remove (priv->pending_id); +- else +- nm_warning ("Timeout reached, but there's nothing to time out"); +- +- return FALSE; +-} +- +-static void +-nm_serial_device_add_timeout (NMSerialDevice *self, guint timeout) +-{ +- NMSerialDevicePrivate *priv = NM_SERIAL_DEVICE_GET_PRIVATE (self); +- +- if (priv->pending_id == 0) +- nm_warning ("Adding a time out while not waiting for any data"); +- +- if (priv->timeout_id) { +- nm_warning ("Trying to add a new time out while the old one still exists"); +- g_source_remove (priv->timeout_id); +- } +- +- priv->timeout_id = g_timeout_add_full (G_PRIORITY_DEFAULT, +- timeout * 1000, +- nm_serial_device_timed_out, +- self, +- nm_serial_device_timeout_removed); +- if (G_UNLIKELY (priv->timeout_id == 0)) +- nm_warning ("Registering serial device time out failed."); +-} +- +-static void +-nm_serial_device_remove_timeout (NMSerialDevice *self) +-{ +- NMSerialDevicePrivate *priv = NM_SERIAL_DEVICE_GET_PRIVATE (self); +- +- if (priv->timeout_id) +- g_source_remove (priv->timeout_id); +-} +- +-/* Pending data reading */ +- +-static guint +-nm_serial_device_set_pending (NMSerialDevice *device, +- guint timeout, +- GIOFunc callback, +- gpointer user_data, +- GDestroyNotify notify) +-{ +- NMSerialDevicePrivate *priv = NM_SERIAL_DEVICE_GET_PRIVATE (device); +- +- if (G_UNLIKELY (priv->pending_id)) { +- /* FIXME: Probably should queue up pending calls instead? */ +- /* Multiple pending calls on the same GIOChannel doesn't work, so let's cancel the previous one. */ +- nm_warning ("Adding new pending call while previous one isn't finished."); +- nm_warning ("Cancelling the previous pending call."); +- g_source_remove (priv->pending_id); +- } +- +- priv->pending_id = g_io_add_watch_full (priv->channel, +- G_PRIORITY_DEFAULT, +- G_IO_IN | G_IO_ERR | G_IO_HUP, +- callback, user_data, notify); +- +- nm_serial_device_add_timeout (device, timeout); +- +- return priv->pending_id; +-} +- +-static void +-nm_serial_device_pending_done (NMSerialDevice *self) +-{ +- NM_SERIAL_DEVICE_GET_PRIVATE (self)->pending_id = 0; +- nm_serial_device_remove_timeout (self); +-} +- +-/****/ +- +-static gboolean +-config_fd (NMSerialDevice *device, NMSettingSerial *setting) +-{ +- NMSerialDevicePrivate *priv = NM_SERIAL_DEVICE_GET_PRIVATE (device); +- struct termio stbuf; +- int speed; +- int bits; +- int parity; +- int stopbits; +- +- speed = parse_baudrate (nm_setting_serial_get_baud (setting)); +- bits = parse_bits (nm_setting_serial_get_bits (setting)); +- parity = parse_parity (nm_setting_serial_get_parity (setting)); +- stopbits = parse_stopbits (nm_setting_serial_get_stopbits (setting)); +- +- ioctl (priv->fd, TCGETA, &stbuf); +- +- stbuf.c_iflag &= ~(IGNCR | ICRNL | IUCLC | INPCK | IXON | IXANY | IGNPAR ); +- stbuf.c_oflag &= ~(OPOST | OLCUC | OCRNL | ONLCR | ONLRET); +- stbuf.c_lflag &= ~(ICANON | XCASE | ECHO | ECHOE | ECHONL); +- stbuf.c_lflag &= ~(ECHO | ECHOE); +- stbuf.c_cc[VMIN] = 1; +- stbuf.c_cc[VTIME] = 0; +- stbuf.c_cc[VEOF] = 1; +- +- stbuf.c_cflag &= ~(CBAUD | CSIZE | CSTOPB | CLOCAL | PARENB); +- stbuf.c_cflag |= (speed | bits | CREAD | 0 | parity | stopbits); +- +- if (ioctl (priv->fd, TCSETA, &stbuf) < 0) { +- nm_warning ("(%s) cannot control device (errno %d)", +- nm_device_get_iface (NM_DEVICE (device)), errno); +- return FALSE; +- } +- +- return TRUE; +-} +- +-gboolean +-nm_serial_device_open (NMSerialDevice *device, +- NMSettingSerial *setting) +-{ +- NMSerialDevicePrivate *priv; +- const char *iface; +- char *path; +- +- g_return_val_if_fail (NM_IS_SERIAL_DEVICE (device), FALSE); +- g_return_val_if_fail (NM_IS_SETTING_SERIAL (setting), FALSE); +- +- priv = NM_SERIAL_DEVICE_GET_PRIVATE (device); +- iface = nm_device_get_iface (NM_DEVICE (device)); +- +- nm_debug ("(%s) opening device...", iface); +- +- path = g_build_filename ("/dev", iface, NULL); +- priv->fd = open (path, O_RDWR | O_EXCL | O_NONBLOCK | O_NOCTTY); +- g_free (path); +- +- if (priv->fd < 0) { +- nm_warning ("(%s) cannot open device (errno %d)", iface, errno); +- return FALSE; +- } +- +- if (ioctl (priv->fd, TCGETA, &priv->old_t) < 0) { +- nm_warning ("(%s) cannot control device (errno %d)", iface, errno); +- close (priv->fd); +- return FALSE; +- } +- +- if (!config_fd (device, setting)) { +- close (priv->fd); +- return FALSE; +- } +- +- priv->channel = g_io_channel_unix_new (priv->fd); +- +- return TRUE; +-} +- +-void +-nm_serial_device_close (NMSerialDevice *device) +-{ +- NMSerialDevicePrivate *priv; +- +- g_return_if_fail (NM_IS_SERIAL_DEVICE (device)); +- +- priv = NM_SERIAL_DEVICE_GET_PRIVATE (device); +- +- if (priv->pending_id) +- g_source_remove (priv->pending_id); +- +- if (priv->ppp_manager) { +- nm_ppp_manager_stop (priv->ppp_manager); +- g_object_unref (priv->ppp_manager); +- priv->ppp_manager = NULL; +- } +- +- if (priv->fd) { +- nm_debug ("Closing device '%s'", nm_device_get_iface (NM_DEVICE (device))); +- +- if (priv->channel) { +- g_io_channel_unref (priv->channel); +- priv->channel = NULL; +- } +- +- ioctl (priv->fd, TCSETA, &priv->old_t); +- close (priv->fd); +- priv->fd = 0; +- } +-} +- +-gboolean +-nm_serial_device_send_command (NMSerialDevice *device, GByteArray *command) +-{ +- int fd; +- NMSettingSerial *setting; +- int i, eagain_count = 1000; +- ssize_t written; +- guint32 send_delay = 0; +- +- g_return_val_if_fail (NM_IS_SERIAL_DEVICE (device), FALSE); +- g_return_val_if_fail (command != NULL, FALSE); +- +- fd = NM_SERIAL_DEVICE_GET_PRIVATE (device)->fd; +- setting = NM_SETTING_SERIAL (serial_device_get_setting (device, NM_TYPE_SETTING_SERIAL)); +- if (setting) +- send_delay = nm_setting_serial_get_send_delay (setting); +- if (send_delay == 0) +- send_delay = G_USEC_PER_SEC / 1000; +- +- nm_serial_debug ("Sending:", (char *) command->data, command->len); +- +- for (i = 0; i < command->len && eagain_count > 0;) { +- written = write (fd, command->data + i, 1); +- +- if (written > 0) +- i += written; +- else { +- /* Treat written == 0 as EAGAIN to ensure we break out of the +- * for() loop eventually. +- */ +- if ((written < 0) && (errno != EAGAIN)) { +- g_warning ("Error in writing (errno %d)", errno); +- return FALSE; +- } +- eagain_count--; +- } +- g_usleep (send_delay); +- } +- +- if (eagain_count <= 0) +- nm_serial_debug ("Error: too many retries sending:", (char *) command->data, command->len); +- +- return TRUE; +-} +- +-gboolean +-nm_serial_device_send_command_string (NMSerialDevice *device, const char *str) +-{ +- GByteArray *command; +- gboolean ret; +- +- g_return_val_if_fail (NM_IS_SERIAL_DEVICE (device), FALSE); +- g_return_val_if_fail (str != NULL, FALSE); +- +- command = g_byte_array_new (); +- g_byte_array_append (command, (guint8 *) str, strlen (str)); +- g_byte_array_append (command, (guint8 *) "\r", 1); +- +- ret = nm_serial_device_send_command (device, command); +- g_byte_array_free (command, TRUE); +- +- return ret; +-} +- +-static gboolean +-find_terminator (const char *line, const char **terminators) +-{ +- int i; +- +- for (i = 0; terminators[i]; i++) { +- if (!strncasecmp (line, terminators[i], strlen (terminators[i]))) +- return TRUE; +- } +- return FALSE; +-} +- +-static const char * +-find_response (const char *line, const char **responses, gint *idx) +-{ +- int i; +- +- /* Don't look for a result again if we got one previously */ +- for (i = 0; responses[i]; i++) { +- if (strcasestr (line, responses[i])) { +- *idx = i; +- return line; +- } +- } +- return NULL; +-} +- +-#define RESPONSE_LINE_MAX 128 +- +-int +-nm_serial_device_wait_reply_blocking (NMSerialDevice *device, +- guint32 timeout_secs, +- const char **needles, +- const char **terminators) +-{ +- char buf[SERIAL_BUF_SIZE + 1]; +- int fd, reply_index = -1, bytes_read; +- GString *result = NULL; +- time_t end; +- const char *response = NULL; +- gboolean done = FALSE; +- +- g_return_val_if_fail (NM_IS_SERIAL_DEVICE (device), -1); +- g_return_val_if_fail (timeout_secs <= 60, -1); +- g_return_val_if_fail (needles != NULL, -1); +- +- fd = NM_SERIAL_DEVICE_GET_PRIVATE (device)->fd; +- if (fd < 0) +- return -1; +- +- end = time (NULL) + timeout_secs; +- result = g_string_sized_new (20); +- do { +- bytes_read = read (fd, buf, SERIAL_BUF_SIZE); +- if (bytes_read < 0 && errno != EAGAIN) { +- nm_warning ("%s: read error: %d (%s)", +- nm_device_get_iface (NM_DEVICE (device)), +- errno, +- strerror (errno)); +- return -1; +- } +- +- if (bytes_read == 0) +- break; /* EOF */ +- else if (bytes_read > 0) { +- buf[bytes_read] = 0; +- g_string_append (result, buf); +- +- nm_serial_debug ("Got:", result->str, result->len); +- } +- +- /* Look for needles and terminators */ +- if ((bytes_read > 0) && result->str) { +- char *p = result->str; +- +- /* Break the response up into lines and process each one */ +- while ((p < result->str + strlen (result->str)) && !done) { +- char line[RESPONSE_LINE_MAX] = { '\0', }; +- char *tmp; +- int i; +- gboolean got_something = FALSE; +- +- for (i = 0; *p && (i < RESPONSE_LINE_MAX - 1); p++) { +- /* Ignore front CR/LF */ +- if ((*p == '\n') || (*p == '\r')) { +- if (got_something) +- break; +- } else { +- line[i++] = *p; +- got_something = TRUE; +- } +- } +- line[i] = '\0'; +- +- tmp = g_strstrip (line); +- if (tmp && strlen (tmp)) { +- done = find_terminator (tmp, terminators); +- if (reply_index == -1) +- response = find_response (tmp, needles, &reply_index); +- } +- } +- } +- +- /* Limit the size of the buffer */ +- if (result->len > SERIAL_BUF_SIZE) { +- g_warning ("%s (%s): response buffer filled before repsonse received", +- __func__, nm_device_get_iface (NM_DEVICE (device))); +- break; +- } +- +- if (!done) +- g_usleep (100); +- } while (!done && (time (NULL) < end)); +- +- return reply_index; +-} +- +-typedef struct { +- NMSerialDevice *device; +- char **str_needles; +- char **terminators; +- GString *result; +- NMSerialWaitForReplyFn callback; +- gpointer user_data; +- int reply_index; +- char *reply_line; +- time_t end; +-} WaitForReplyInfo; +- +-static void +-wait_for_reply_done (gpointer data) +-{ +- WaitForReplyInfo *info = (WaitForReplyInfo *) data; +- +- nm_serial_device_pending_done (info->device); +- +- /* Call the callback */ +- info->callback (info->device, info->reply_index, info->reply_line, info->user_data); +- +- /* Free info */ +- if (info->result) +- g_string_free (info->result, TRUE); +- +- g_free (info->reply_line); +- +- g_strfreev (info->str_needles); +- g_strfreev (info->terminators); +- g_slice_free (WaitForReplyInfo, info); +-} +- +-static gboolean +-wait_for_reply_got_data (GIOChannel *source, +- GIOCondition condition, +- gpointer data) +-{ +- WaitForReplyInfo *info = (WaitForReplyInfo *) data; +- gchar buf[SERIAL_BUF_SIZE + 1]; +- gsize bytes_read; +- GIOStatus status; +- gboolean done = FALSE; +- +- if (condition & G_IO_HUP || condition & G_IO_ERR) +- return FALSE; +- +- do { +- GError *err = NULL; +- +- status = g_io_channel_read_chars (source, buf, SERIAL_BUF_SIZE, &bytes_read, &err); +- if (status == G_IO_STATUS_ERROR) { +- g_warning ("%s", err->message); +- g_error_free (err); +- err = NULL; +- } +- +- if (bytes_read > 0) { +- buf[bytes_read] = 0; +- g_string_append (info->result, buf); +- +- nm_serial_debug ("Got:", info->result->str, info->result->len); +- } +- +- /* Look for needles and terminators */ +- if ((bytes_read > 0) && info->result->str) { +- char *p = info->result->str; +- +- /* Break the response up into lines and process each one */ +- while ((p < info->result->str + strlen (info->result->str)) && !done) { +- char line[RESPONSE_LINE_MAX] = { '\0', }; +- char *tmp; +- int i; +- gboolean got_something = FALSE; +- +- for (i = 0; *p && (i < RESPONSE_LINE_MAX - 1); p++) { +- /* Ignore front CR/LF */ +- if ((*p == '\n') || (*p == '\r')) { +- if (got_something) +- break; +- } else { +- line[i++] = *p; +- got_something = TRUE; +- } +- } +- line[i] = '\0'; +- +- tmp = g_strstrip (line); +- if (tmp && strlen (tmp)) { +- done = find_terminator (tmp, (const char **) info->terminators); +- if (info->reply_index == -1) { +- if (find_response (tmp, (const char **) info->str_needles, &(info->reply_index))) +- info->reply_line = g_strdup (tmp); +- } +- } +- } +- } +- +- /* Limit the size of the buffer */ +- if (info->result->len > SERIAL_BUF_SIZE) { +- nm_warning ("(%s): response buffer filled before repsonse received", +- nm_device_get_iface (NM_DEVICE (info->device))); +- done = TRUE; +- break; +- } +- +- /* Make sure we don't go over the timeout, in addition to the timeout +- * handler that's been scheduled. If for some reason this loop doesn't +- * terminate (terminator not found, whatever) then this should make +- * sure that NM doesn't spin the CPU forever. +- */ +- if (time (NULL) > info->end) { +- done = TRUE; +- break; +- } else if (!done) +- g_usleep (50); +- } while (!done || bytes_read == SERIAL_BUF_SIZE || status == G_IO_STATUS_AGAIN); +- +- return !done; +-} +- +-guint +-nm_serial_device_wait_for_reply (NMSerialDevice *device, +- guint timeout, +- const char **responses, +- const char **terminators, +- NMSerialWaitForReplyFn callback, +- gpointer user_data) +-{ +- WaitForReplyInfo *info; +- +- g_return_val_if_fail (NM_IS_SERIAL_DEVICE (device), 0); +- g_return_val_if_fail (responses != NULL, 0); +- g_return_val_if_fail (callback != NULL, 0); +- +- info = g_slice_new0 (WaitForReplyInfo); +- info->device = device; +- info->str_needles = g_strdupv ((char **) responses); +- info->terminators = g_strdupv ((char **) terminators); +- info->result = g_string_new (NULL); +- info->callback = callback; +- info->user_data = user_data; +- info->reply_index = -1; +- info->end = time (NULL) + timeout; +- +- return nm_serial_device_set_pending (device, timeout, wait_for_reply_got_data, info, wait_for_reply_done); +-} +- +-#if 0 +-typedef struct { +- NMSerialDevice *device; +- gboolean timed_out; +- NMSerialWaitQuietFn callback; +- gpointer user_data; +-} WaitQuietInfo; +- +-static void +-wait_quiet_done (gpointer data) +-{ +- WaitQuietInfo *info = (WaitQuietInfo *) data; +- +- nm_serial_device_pending_done (info->device); +- +- /* Call the callback */ +- info->callback (info->device, info->timed_out, info->user_data); +- +- /* Free info */ +- g_slice_free (WaitQuietInfo, info); +-} +- +-static gboolean +-wait_quiet_quiettime (gpointer data) +-{ +- WaitQuietInfo *info = (WaitQuietInfo *) data; +- +- info->timed_out = FALSE; +- g_source_remove (NM_SERIAL_DEVICE_GET_PRIVATE (info->device)->pending); +- +- return FALSE; +-} +- +-static gboolean +-wait_quiet_got_data (GIOChannel *source, +- GIOCondition condition, +- gpointer data) +-{ +- WaitQuietInfo *info = (WaitQuietInfo *) data; +- gsize bytes_read; +- char buf[4096]; +- GIOStatus status; +- +- if (condition & G_IO_HUP || condition & G_IO_ERR) +- return FALSE; +- +- if (condition & G_IO_IN) { +- do { +- status = g_io_channel_read_chars (source, buf, 4096, &bytes_read, NULL); +- +- if (bytes_read) { +- /* Reset the quiet time timeout */ +- g_source_remove (info->quiet_id); +- info->quiet_id = g_timeout_add (info->quiet_time, wait_quiet_quiettime, info); +- } +- } while (bytes_read == 4096 || status == G_IO_STATUS_AGAIN); +- } +- +- return TRUE; +-} +- +-void +-nm_serial_device_wait_quiet (NMSerialDevice *device, +- guint timeout, +- guint quiet_time, +- NMSerialWaitQuietFn callback, +- gpointer user_data) +-{ +- WaitQuietInfo *info; +- +- g_return_if_fail (NM_IS_SERIAL_DEVICE (device)); +- g_return_if_fail (callback != NULL); +- +- info = g_slice_new0 (WaitQuietInfo); +- info->device = device; +- info->timed_out = TRUE; +- info->callback = callback; +- info->user_data = user_data; +- info->quiet_id = g_timeout_add (quiet_time, +- wait_quiet_timeout, +- info); +- +- return nm_serial_device_set_pending (device, timeout, wait_quiet_got_data, info, wait_quiet_done); +-} +- +-#endif +- +-typedef struct { +- NMSerialDevice *device; +- speed_t current_speed; +- NMSerialFlashFn callback; +- gpointer user_data; +-} FlashInfo; +- +-static speed_t +-get_speed (NMSerialDevice *device) +-{ +- struct termios options; +- +- tcgetattr (NM_SERIAL_DEVICE_GET_PRIVATE (device)->fd, &options); +- +- return cfgetospeed (&options); +-} +- +-static void +-set_speed (NMSerialDevice *device, speed_t speed) +-{ +- struct termios options; +- int fd; +- +- fd = NM_SERIAL_DEVICE_GET_PRIVATE (device)->fd; +- tcgetattr (fd, &options); +- +- cfsetispeed (&options, speed); +- cfsetospeed (&options, speed); +- +- options.c_cflag |= (CLOCAL | CREAD); +- tcsetattr (fd, TCSANOW, &options); +-} +- +-static void +-flash_done (gpointer data) +-{ +- FlashInfo *info = (FlashInfo *) data; +- +- NM_SERIAL_DEVICE_GET_PRIVATE (info->device)->pending_id = 0; +- +- info->callback (info->device, info->user_data); +- +- g_slice_free (FlashInfo, info); +-} +- +-static gboolean +-flash_do (gpointer data) +-{ +- FlashInfo *info = (FlashInfo *) data; +- +- set_speed (info->device, info->current_speed); +- +- return FALSE; +-} +- +-guint +-nm_serial_device_flash (NMSerialDevice *device, +- guint32 flash_time, +- NMSerialFlashFn callback, +- gpointer user_data) +-{ +- FlashInfo *info; +- guint id; +- +- g_return_val_if_fail (NM_IS_SERIAL_DEVICE (device), 0); +- g_return_val_if_fail (callback != NULL, 0); +- +- info = g_slice_new0 (FlashInfo); +- info->device = device; +- info->current_speed = get_speed (device); +- info->callback = callback; +- info->user_data = user_data; +- +- set_speed (device, B0); +- +- id = g_timeout_add_full (G_PRIORITY_DEFAULT, +- flash_time, +- flash_do, +- info, +- flash_done); +- +- NM_SERIAL_DEVICE_GET_PRIVATE (device)->pending_id = id; +- +- return id; +-} +- +-GIOChannel * +-nm_serial_device_get_io_channel (NMSerialDevice *device) +-{ +- NMSerialDevicePrivate *priv; +- +- g_return_val_if_fail (NM_IS_SERIAL_DEVICE (device), 0); +- +- priv = NM_SERIAL_DEVICE_GET_PRIVATE (device); +- if (priv->channel) +- return g_io_channel_ref (priv->channel); +- +- return NULL; +-} +- +-NMPPPManager * +-nm_serial_device_get_ppp_manager (NMSerialDevice *device) +-{ +- g_return_val_if_fail (NM_IS_SERIAL_DEVICE (device), NULL); +- +- return NM_SERIAL_DEVICE_GET_PRIVATE (device)->ppp_manager; +-} +- +-static void +-ppp_state_changed (NMPPPManager *ppp_manager, NMPPPStatus status, gpointer user_data) +-{ +- NMDevice *device = NM_DEVICE (user_data); +- +- switch (status) { +- case NM_PPP_STATUS_NETWORK: +- nm_device_state_changed (device, NM_DEVICE_STATE_IP_CONFIG, NM_DEVICE_STATE_REASON_NONE); +- break; +- case NM_PPP_STATUS_DISCONNECT: +- nm_device_state_changed (device, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_PPP_DISCONNECT); +- break; +- case NM_PPP_STATUS_DEAD: +- nm_device_state_changed (device, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_PPP_FAILED); +- break; +- case NM_PPP_STATUS_AUTHENTICATE: +- nm_device_state_changed (device, NM_DEVICE_STATE_NEED_AUTH, NM_DEVICE_STATE_REASON_NONE); +- break; +- default: +- break; +- } +-} +- +-static void +-ppp_ip4_config (NMPPPManager *ppp_manager, +- const char *iface, +- NMIP4Config *config, +- gpointer user_data) +-{ +- NMDevice *device = NM_DEVICE (user_data); +- +- nm_device_set_ip_iface (device, iface); +- NM_SERIAL_DEVICE_GET_PRIVATE (device)->pending_ip4_config = g_object_ref (config); +- nm_device_activate_schedule_stage4_ip_config_get (device); +-} +- +-static void +-ppp_stats (NMPPPManager *ppp_manager, +- guint32 in_bytes, +- guint32 out_bytes, +- gpointer user_data) +-{ +- NMSerialDevice *device = NM_SERIAL_DEVICE (user_data); +- NMSerialDevicePrivate *priv = NM_SERIAL_DEVICE_GET_PRIVATE (device); +- +- if (priv->in_bytes != in_bytes || priv->out_bytes != out_bytes) { +- priv->in_bytes = in_bytes; +- priv->out_bytes = out_bytes; +- +- g_signal_emit (device, signals[PPP_STATS], 0, in_bytes, out_bytes); +- } +-} +- +-static NMActStageReturn +-real_act_stage2_config (NMDevice *device, NMDeviceStateReason *reason) +-{ +- NMSerialDevicePrivate *priv = NM_SERIAL_DEVICE_GET_PRIVATE (device); +- NMSerialDeviceClass *serial_class = NM_SERIAL_DEVICE_GET_CLASS (device); +- NMActRequest *req; +- GError *err = NULL; +- NMActStageReturn ret; +- const char *ppp_name = NULL; +- +- req = nm_device_get_act_request (device); +- g_assert (req); +- +- if (serial_class->get_ppp_name) +- ppp_name = serial_class->get_ppp_name (NM_SERIAL_DEVICE (device), req); +- +- priv->ppp_manager = nm_ppp_manager_new (nm_device_get_iface (device)); +- if (nm_ppp_manager_start (priv->ppp_manager, req, ppp_name, &err)) { +- g_signal_connect (priv->ppp_manager, "state-changed", +- G_CALLBACK (ppp_state_changed), +- device); +- g_signal_connect (priv->ppp_manager, "ip4-config", +- G_CALLBACK (ppp_ip4_config), +- device); +- g_signal_connect (priv->ppp_manager, "stats", +- G_CALLBACK (ppp_stats), +- device); +- +- ret = NM_ACT_STAGE_RETURN_POSTPONE; +- } else { +- nm_warning ("%s", err->message); +- g_error_free (err); +- +- g_object_unref (priv->ppp_manager); +- priv->ppp_manager = NULL; +- +- *reason = NM_DEVICE_STATE_REASON_PPP_START_FAILED; +- ret = NM_ACT_STAGE_RETURN_FAILURE; +- } +- +- return ret; +-} +- +-static NMActStageReturn +-real_act_stage4_get_ip4_config (NMDevice *device, +- NMIP4Config **config, +- NMDeviceStateReason *reason) +-{ +- NMSerialDevicePrivate *priv = NM_SERIAL_DEVICE_GET_PRIVATE (device); +- NMConnection *connection; +- NMSettingIP4Config *s_ip4; +- +- g_return_val_if_fail (config != NULL, NM_ACT_STAGE_RETURN_FAILURE); +- g_return_val_if_fail (*config == NULL, NM_ACT_STAGE_RETURN_FAILURE); +- g_return_val_if_fail (reason != NULL, NM_ACT_STAGE_RETURN_FAILURE); +- +- connection = nm_act_request_get_connection (nm_device_get_act_request (device)); +- g_assert (connection); +- +- s_ip4 = (NMSettingIP4Config *) nm_connection_get_setting (connection, NM_TYPE_SETTING_IP4_CONFIG); +- +- *config = priv->pending_ip4_config; +- priv->pending_ip4_config = NULL; +- nm_utils_merge_ip4_config (*config, s_ip4); +- +- return NM_ACT_STAGE_RETURN_SUCCESS; +-} +- +-static void +-cleanup_device (NMSerialDevice *device) +-{ +- NMSerialDevicePrivate *priv = NM_SERIAL_DEVICE_GET_PRIVATE (device); +- +- nm_device_set_ip_iface (NM_DEVICE (device), NULL); +- +- if (priv->pending_ip4_config) { +- g_object_unref (priv->pending_ip4_config); +- priv->pending_ip4_config = NULL; +- } +- +- priv->in_bytes = priv->out_bytes = 0; +-} +- +-static void +-real_deactivate_quickly (NMDevice *device) +-{ +- NMSerialDevice *self = NM_SERIAL_DEVICE (device); +- +- cleanup_device (self); +- nm_serial_device_close (self); +-} +- +-static guint32 +-real_get_generic_capabilities (NMDevice *dev) +-{ +- return NM_DEVICE_CAP_NM_SUPPORTED; +-} +- +-/*****************************************************************************/ +- +-static void +-nm_serial_device_init (NMSerialDevice *self) +-{ +- if (getenv ("NM_SERIAL_DEBUG")) +- serial_debug = TRUE; +-} +- +-static void +-finalize (GObject *object) +-{ +- NMSerialDevice *self = NM_SERIAL_DEVICE (object); +- +- cleanup_device (self); +- nm_serial_device_close (self); +- +- G_OBJECT_CLASS (nm_serial_device_parent_class)->finalize (object); +-} +- +-static void +-nm_serial_device_class_init (NMSerialDeviceClass *klass) +-{ +- GObjectClass *object_class = G_OBJECT_CLASS (klass); +- NMDeviceClass *parent_class = NM_DEVICE_CLASS (klass); +- +- g_type_class_add_private (object_class, sizeof (NMSerialDevicePrivate)); +- +- /* Virtual methods */ +- object_class->finalize = finalize; +- +- parent_class->get_generic_capabilities = real_get_generic_capabilities; +- parent_class->act_stage2_config = real_act_stage2_config; +- parent_class->act_stage4_get_ip4_config = real_act_stage4_get_ip4_config; +- parent_class->deactivate_quickly = real_deactivate_quickly; +- +- /* Signals */ +- signals[PPP_STATS] = +- g_signal_new ("ppp-stats", +- G_OBJECT_CLASS_TYPE (object_class), +- G_SIGNAL_RUN_FIRST, +- G_STRUCT_OFFSET (NMSerialDeviceClass, ppp_stats), +- NULL, NULL, +- _nm_marshal_VOID__UINT_UINT, +- G_TYPE_NONE, 2, +- G_TYPE_UINT, G_TYPE_UINT); +- +- dbus_g_object_type_install_info (G_TYPE_FROM_CLASS (klass), +- &dbus_glib_nm_serial_device_object_info); +-} +diff --git a/src/nm-serial-device.h b/src/nm-serial-device.h +deleted file mode 100644 +index 6e9b53b..0000000 +--- a/src/nm-serial-device.h ++++ /dev/null +@@ -1,110 +0,0 @@ +-/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +-/* NetworkManager -- Network link manager +- * +- * 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. +- * +- * 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., +- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +- * +- * Copyright (C) 2007 - 2008 Novell, Inc. +- * Copyright (C) 2007 - 2008 Red Hat, Inc. +- */ +- +-#ifndef NM_SERIAL_DEVICE_H +-#define NM_SERIAL_DEVICE_H +- +-#include <nm-device.h> +-#include <nm-setting-serial.h> +-#include "ppp-manager/nm-ppp-manager.h" +- +-G_BEGIN_DECLS +- +-#define NM_TYPE_SERIAL_DEVICE (nm_serial_device_get_type ()) +-#define NM_SERIAL_DEVICE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_SERIAL_DEVICE, NMSerialDevice)) +-#define NM_SERIAL_DEVICE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_SERIAL_DEVICE, NMSerialDeviceClass)) +-#define NM_IS_SERIAL_DEVICE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_SERIAL_DEVICE)) +-#define NM_IS_SERIAL_DEVICE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_SERIAL_DEVICE)) +-#define NM_SERIAL_DEVICE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_SERIAL_DEVICE, NMSerialDeviceClass)) +- +-typedef struct { +- NMDevice parent; +-} NMSerialDevice; +- +-typedef struct { +- NMDeviceClass parent; +- +- const char * (*get_ppp_name) (NMSerialDevice *device, NMActRequest *req); +- +- /* Signals */ +- void (*ppp_stats) (NMSerialDevice *device, guint32 in_bytes, guint32 out_bytes); +-} NMSerialDeviceClass; +- +-GType nm_serial_device_get_type (void); +- +-typedef void (*NMSerialGetReplyFn) (NMSerialDevice *device, +- const char *reply, +- gpointer user_data); +- +-typedef void (*NMSerialWaitForReplyFn) (NMSerialDevice *device, +- int reply_index, +- const char *reply, +- gpointer user_data); +- +-typedef void (*NMSerialWaitQuietFn) (NMSerialDevice *device, +- gboolean timed_out, +- gpointer user_data); +- +-typedef void (*NMSerialFlashFn) (NMSerialDevice *device, +- gpointer user_data); +- +- +- +-gboolean nm_serial_device_open (NMSerialDevice *device, +- NMSettingSerial *setting); +- +-void nm_serial_device_close (NMSerialDevice *device); +-gboolean nm_serial_device_send_command (NMSerialDevice *device, +- GByteArray *command); +- +-gboolean nm_serial_device_send_command_string (NMSerialDevice *device, +- const char *str); +- +-int nm_serial_device_wait_reply_blocking (NMSerialDevice *device, +- guint32 timeout_secs, +- const char **needles, +- const char **terminators); +- +-guint nm_serial_device_wait_for_reply (NMSerialDevice *device, +- guint timeout, +- const char **responses, +- const char **terminators, +- NMSerialWaitForReplyFn callback, +- gpointer user_data); +- +-void nm_serial_device_wait_quiet (NMSerialDevice *device, +- guint timeout, +- guint quiet_time, +- NMSerialWaitQuietFn callback, +- gpointer user_data); +- +-guint nm_serial_device_flash (NMSerialDevice *device, +- guint32 flash_time, +- NMSerialFlashFn callback, +- gpointer user_data); +- +-GIOChannel *nm_serial_device_get_io_channel (NMSerialDevice *device); +- +-NMPPPManager *nm_serial_device_get_ppp_manager (NMSerialDevice *device); +- +-G_END_DECLS +- +-#endif /* NM_SERIAL_DEVICE_H */ diff --git a/obsolete-patches/nm-applet-r1053-use-modem-manager.patch b/obsolete-patches/nm-applet-r1053-use-modem-manager.patch new file mode 100644 index 0000000..ba0a0f7 --- /dev/null +++ b/obsolete-patches/nm-applet-r1053-use-modem-manager.patch @@ -0,0 +1,2253 @@ +commit 48892f9ff93f48fc560e706f47afcaadd002f41e +Author: Tambet Ingo <tambet@gmail.com> +Date: Tue Jan 13 13:36:29 2009 +0200 + + Use ModemManager. + +diff --git a/configure.ac b/configure.ac +index 7524739..db7597a 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -222,6 +222,7 @@ src/gconf-helpers/Makefile + src/wireless-security/Makefile + src/polkit-helpers/Makefile + src/connection-editor/Makefile ++src/modems/Makefile + icons/Makefile + po/Makefile.in + ]) +diff --git a/src/Makefile.am b/src/Makefile.am +index 3486186..33d2c49 100644 +--- a/src/Makefile.am ++++ b/src/Makefile.am +@@ -1,4 +1,4 @@ +-SUBDIRS = marshallers utils gconf-helpers wireless-security polkit-helpers connection-editor ++SUBDIRS = marshallers utils gconf-helpers wireless-security polkit-helpers connection-editor modems + + NULL= + +@@ -49,6 +49,9 @@ nm_applet_SOURCES = \ + applet-device-gsm.c \ + applet-device-cdma.h \ + applet-device-cdma.c \ ++ mm-types.h \ ++ nma-gsm-modem.c \ ++ nma-gsm-modem.h \ + $(NULL) + + nm_applet_LDADD = \ +diff --git a/src/applet-device-gsm.c b/src/applet-device-gsm.c +index eaacf48..2a3d8bc 100644 +--- a/src/applet-device-gsm.c ++++ b/src/applet-device-gsm.c +@@ -24,6 +24,9 @@ + #include <config.h> + #endif + ++#include <sys/types.h> ++#include <unistd.h> ++ + #include <glib/gi18n.h> + #include <gtk/gtkwidget.h> + #include <gtk/gtkmenuitem.h> +@@ -39,6 +42,8 @@ + + #include "applet.h" + #include "applet-device-gsm.h" ++#include "nma-gsm-modem.h" ++#include "mm-types.h" + #include "utils.h" + + typedef struct { +@@ -174,6 +179,50 @@ add_default_connection_item (NMDevice *device, + } + + static void ++child_setup (gpointer user_data G_GNUC_UNUSED) ++{ ++ /* We are in the child process at this point */ ++ pid_t pid = getpid (); ++ setpgid (pid, pid); ++} ++ ++static void ++gsm_properties_cb (GtkMenuItem *mi, gpointer user_data) ++{ ++ NMDevice *device = NM_DEVICE (user_data); ++ char *argv[3]; ++ GError *error = NULL; ++ gboolean success; ++ ++ argv[0] = BINDIR "/nm-modem-properties"; ++ argv[1] = (char *) nm_device_get_udi (device); ++ argv[2] = NULL; ++ ++ success = g_spawn_async ("/", argv, NULL, 0, &child_setup, NULL, NULL, &error); ++ if (!success) { ++ g_warning ("Error launching modem properties dialog: %s", error->message); ++ g_error_free (error); ++ } ++} ++ ++static void ++add_properties_item (NMDevice *device, ++ GtkWidget *menu) ++{ ++ GtkWidget *item; ++ ++ if (nm_device_get_state (device) != NM_DEVICE_STATE_DISCONNECTED) ++ return; ++ ++ item = gtk_menu_item_new_with_label (_("Properties")); ++ g_signal_connect (item, "activate", ++ G_CALLBACK (gsm_properties_cb), ++ device); ++ ++ gtk_menu_shell_append (GTK_MENU_SHELL (menu), item); ++} ++ ++static void + gsm_menu_item_deactivate (GtkMenuItem *item, gpointer user_data) + { + GSMMenuItemInfo *info = (GSMMenuItemInfo *) user_data; +@@ -273,41 +322,67 @@ gsm_add_menu_item (NMDevice *device, + add_connection_items (device, connections, active, menu, applet); + else + add_default_connection_item (device, menu, applet); ++ + add_disconnect_item (device, menu, applet); ++ add_properties_item (device, menu); + + out: + g_slist_free (connections); + } + + static void ++signal_quality_changed (NMAGsmModem *modem, guint32 quality, gpointer user_data) ++{ ++ applet_schedule_update_icon (NM_APPLET (user_data)); ++} ++ ++static void + gsm_device_state_changed (NMDevice *device, + NMDeviceState new_state, + NMDeviceState old_state, + NMDeviceStateReason reason, + NMApplet *applet) + { +- if (new_state == NM_DEVICE_STATE_ACTIVATED) { +- NMConnection *connection; +- NMSettingConnection *s_con = NULL; +- char *str = NULL; +- +- connection = applet_find_active_connection_for_device (device, applet, NULL); +- if (connection) { +- const char *id; +- +- s_con = NM_SETTING_CONNECTION (nm_connection_get_setting (connection, NM_TYPE_SETTING_CONNECTION)); +- id = s_con ? nm_setting_connection_get_id (s_con) : NULL; +- if (id) +- str = g_strdup_printf (_("You are now connected to '%s'."), id); +- } ++ NMAGsmModem *modem; ++ char *oper_code; ++ char *oper_name; ++ char *msg; ++ guint32 reg_status; ++ ++ if (new_state != NM_DEVICE_STATE_ACTIVATED) ++ return; + +- applet_do_notify_with_pref (applet, +- _("Connection Established"), +- str ? str : _("You are now connected to the GSM network."), +- "nm-device-wwan", +- PREF_DISABLE_CONNECTED_NOTIFICATIONS); +- g_free (str); ++ modem = (NMAGsmModem *) g_object_get_data (G_OBJECT (device), "gsm-modem"); ++ if (!modem) { ++ DBusGConnection *bus; ++ ++ bus = dbus_g_bus_get (DBUS_BUS_SYSTEM, NULL); ++ if (!bus) ++ return; ++ ++ modem = nma_gsm_modem_new (bus, nm_device_get_udi (device)); ++ dbus_g_connection_unref (bus); ++ ++ g_object_set_data_full (G_OBJECT (device), "gsm-modem", modem, g_object_unref); ++ ++ g_signal_connect (modem, "signal-quality", ++ G_CALLBACK (signal_quality_changed), ++ applet); + } ++ ++ oper_code = NULL; ++ oper_name = NULL; ++ reg_status = nma_gsm_modem_get_registration_info (modem, &oper_code, &oper_name); ++ msg = g_strdup_printf (_("You are now connected to the %s GSM network '%s'."), ++ reg_status == MM_GSM_MODEM_REG_STATUS_ROAMING ? _("roaming") : _("home"), ++ oper_name); ++ ++ applet_do_notify_with_pref (applet, _("Connection Established"), msg, ++ "nm-device-wwan", PREF_DISABLE_CONNECTED_NOTIFICATIONS); ++ ++ g_free (oper_code); ++ g_free (oper_name); ++ g_free (msg); + } + + static GdkPixbuf * +@@ -318,6 +393,7 @@ gsm_get_icon (NMDevice *device, + NMApplet *applet) + { + NMSettingConnection *s_con; ++ NMAGsmModem *modem; + GdkPixbuf *pixbuf = NULL; + const char *id; + +@@ -341,8 +417,40 @@ gsm_get_icon (NMDevice *device, + *tip = g_strdup_printf (_("Requesting a network address for '%s'..."), id); + break; + case NM_DEVICE_STATE_ACTIVATED: +- pixbuf = applet->wwan_icon; +- *tip = g_strdup_printf (_("Mobile broadband connection '%s' active"), id); ++ modem = (NMAGsmModem *) g_object_get_data (G_OBJECT (device), "gsm-modem"); ++ if (modem) { ++ char *oper_code; ++ char *oper_name; ++ guint32 reg_status; ++ guint32 quality; ++ ++ quality = nma_gsm_modem_get_signal_quality (modem); ++ quality = CLAMP (quality, 0, 100); ++ ++ if (quality > 80) ++ pixbuf = applet->wireless_100_icon; ++ else if (quality > 55) ++ pixbuf = applet->wireless_75_icon; ++ else if (quality > 30) ++ pixbuf = applet->wireless_50_icon; ++ else if (quality > 5) ++ pixbuf = applet->wireless_25_icon; ++ else ++ pixbuf = applet->wireless_00_icon; ++ ++ reg_status = nma_gsm_modem_get_registration_info (modem, &oper_code, &oper_name); ++ *tip = g_strdup_printf (_("%s GSM connection '%s' (%d%%)"), ++ reg_status == MM_GSM_MODEM_REG_STATUS_ROAMING ? _("Roaming") : _("Home"), ++ oper_name, quality); ++ ++ g_free (oper_name); ++ g_free (oper_code); ++ ++ } else { ++ pixbuf = applet->wireless_00_icon; ++ *tip = g_strdup_printf (_("GSM connection")); ++ } ++ + break; + default: + break; +@@ -514,6 +622,7 @@ ask_for_pin_puk (NMDevice *device, + + w = gtk_entry_new (); + *out_secret_entry = GTK_ENTRY (w); ++ gtk_entry_set_visibility (GTK_ENTRY (w), FALSE); + gtk_entry_set_max_length (GTK_ENTRY (w), 4); + gtk_entry_set_width_chars (GTK_ENTRY (w), 4); + gtk_entry_set_activates_default (GTK_ENTRY (w), TRUE); +diff --git a/src/connection-editor/page-mobile.c b/src/connection-editor/page-mobile.c +index 2522ff1..40f2d44 100644 +--- a/src/connection-editor/page-mobile.c ++++ b/src/connection-editor/page-mobile.c +@@ -107,7 +107,6 @@ populate_gsm_ui (CEPageMobile *self, NMConnection *connection) + int type_idx; + GHashTable *secrets; + GValue *value; +- GtkWidget *widget; + const char *s; + + s = nm_setting_gsm_get_number (setting); +@@ -146,17 +145,6 @@ populate_gsm_ui (CEPageMobile *self, NMConnection *connection) + } + gtk_combo_box_set_active (priv->network_type, type_idx); + +- /* Hide network type widgets; not supported yet */ +- gtk_widget_hide (GTK_WIDGET (priv->network_type)); +- widget = glade_xml_get_widget (CE_PAGE (self)->xml, "type_label"); +- gtk_widget_hide (widget); +- +- /* Hide Band widgets; not supported yet */ +- widget = glade_xml_get_widget (CE_PAGE (self)->xml, "mobile_band"); +- gtk_widget_hide (widget); +- widget = glade_xml_get_widget (CE_PAGE (self)->xml, "band_label"); +- gtk_widget_hide (widget); +- + secrets = get_secrets (connection, nm_setting_get_name (priv->setting)); + + s = nm_setting_gsm_get_password (setting); +diff --git a/src/mm-types.h b/src/mm-types.h +new file mode 100644 +index 0000000..a1f9979 +--- /dev/null ++++ b/src/mm-types.h +@@ -0,0 +1,18 @@ ++/* -*- Mode: C; tab-width: 5; indent-tabs-mode: t; c-basic-offset: 5 -*- */ ++ ++#ifndef MM_TYPES_H ++#define MM_TYPES_H ++ ++#define MM_DBUS_SERVICE "org.freedesktop.ModemManager" ++#define MM_DBUS_INTERFACE_MODEM_GSM "org.freedesktop.ModemManager.Modem.Gsm.Network" ++ ++enum { ++ MM_GSM_MODEM_REG_STATUS_IDLE = 0, ++ MM_GSM_MODEM_REG_STATUS_HOME = 1, ++ MM_GSM_MODEM_REG_STATUS_SEARCHING = 2, ++ MM_GSM_MODEM_REG_STATUS_DENIED = 3, ++ MM_GSM_MODEM_REG_STATUS_UNKNOWN = 4, ++ MM_GSM_MODEM_REG_STATUS_ROAMING = 5 ++}; ++ ++#endif /* MM_TYPES_H */ +diff --git a/src/modems/Makefile.am b/src/modems/Makefile.am +new file mode 100644 +index 0000000..206ee52 +--- /dev/null ++++ b/src/modems/Makefile.am +@@ -0,0 +1,25 @@ ++bin_PROGRAMS = nm-modem-properties ++ ++nm_modem_properties_CPPFLAGS = \ ++ $(NMA_CFLAGS) \ ++ -DICONDIR=\""$(datadir)/icons"\" \ ++ -DGLADEDIR=\""$(gladedir)"\" \ ++ -DBINDIR=\""$(bindir)"\" \ ++ -DSYSCONFDIR=\""$(sysconfdir)"\" \ ++ -DLIBDIR=\""$(libdir)"\" \ ++ -DNMALOCALEDIR=\"$(datadir)/locale\" \ ++ $(DBUS_CFLAGS) \ ++ -I${top_srcdir}/src/gconf-helpers ++ ++nm_modem_properties_SOURCES = \ ++ main.c ++ ++nm_modem_properties_LDADD = \ ++ $(top_builddir)/src/gconf-helpers/libgconf-helpers.la \ ++ $(NMA_LIBS) ++ ++gladedir = $(datadir)/nm-applet ++glade_DATA = nm-modem-properties.glade ++ ++CLEANFILES = *.bak *.gladep ++EXTRA_DIST = $(glade_DATA) +diff --git a/src/modems/main.c b/src/modems/main.c +new file mode 100644 +index 0000000..3243a6c +--- /dev/null ++++ b/src/modems/main.c +@@ -0,0 +1,559 @@ ++/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ ++ ++#include <string.h> ++#include <gtk/gtk.h> ++#include <glade/glade.h> ++#include <dbus/dbus-glib.h> ++#include <gconf/gconf-client.h> ++#include <nm-connection.h> ++#include <nm-setting-connection.h> ++#include <nm-setting-gsm.h> ++#include <nm-setting-serial.h> ++#include <nm-setting-ppp.h> ++#include <nm-utils.h> ++#include "gconf-helpers.h" ++ ++#define MM_DBUS_SERVICE "org.freedesktop.ModemManager" ++#define MM_DBUS_PATH "/org/freedesktop/ModemManager" ++#define MM_DBUS_INTERFACE "org.freedesktop.ModemManager" ++#define MM_DBUS_INTERFACE_MODEM "org.freedesktop.ModemManager.Modem" ++ ++#define MM_DBUS_INTERFACE_MODEM_GSM_CARD "org.freedesktop.ModemManager.Modem.Gsm.Card" ++#define MM_DBUS_INTERFACE_MODEM_GSM_NETWORK "org.freedesktop.ModemManager.Modem.Gsm.Network" ++ ++#define MM_MODEM_TYPE_UNKNOWN 0 ++#define MM_MODEM_TYPE_GSM 1 ++#define MM_MODEM_TYPE_CDMA 2 ++ ++#define SCAN_COL_NAME 0 ++#define SCAN_COL_STATUS 1 ++#define SCAN_COL_OPER_ID 2 ++ ++typedef struct { ++ /* UI */ ++ GladeXML *glade_xml; ++ GtkDialog *main_dialog; ++ GtkTreeView *network_list; ++ GtkListStore *network_store; ++ GtkWidget *scan_button; ++ GtkWidget *create_net_button; ++ ++ GtkWidget *scan_dialog; ++ GtkProgressBar *scan_progress_bar; ++ ++ GtkDialog *create_network_dialog; ++ GtkEntry *create_network_name; ++ ++ /* DBus */ ++ DBusGConnection *bus; ++ DBusGProxy *proxy; ++ DBusGProxy *gsm_net_proxy; ++ ++ GMainLoop *main_loop; ++} AppData; ++ ++static void ++get_str_done (DBusGProxy *proxy, DBusGProxyCall *call_id, gpointer user_data) ++{ ++ char *result = NULL; ++ GError *error = NULL; ++ ++ if (!dbus_g_proxy_end_call (proxy, call_id, &error, G_TYPE_STRING, &result, G_TYPE_INVALID)) { ++ g_warning ("%s", error->message); ++ g_error_free (error); ++ } else { ++ gtk_label_set_text (GTK_LABEL (user_data), result); ++ g_free (result); ++ } ++} ++ ++static void ++get_card_info_done (DBusGProxy *proxy, DBusGProxyCall *call_id, gpointer user_data) ++{ ++ GladeXML *glade_xml = GLADE_XML (user_data); ++ char *manufacturer = NULL; ++ char *model = NULL; ++ char *version = NULL; ++ GError *error = NULL; ++ ++ if (!dbus_g_proxy_end_call (proxy, call_id, &error, ++ G_TYPE_STRING, &manufacturer, ++ G_TYPE_STRING, &model, ++ G_TYPE_STRING, &version, ++ G_TYPE_INVALID)) { ++ g_warning ("Couldn't get modem information: %s", error->message); ++ g_error_free (error); ++ } else { ++ gtk_label_set_text (GTK_LABEL (glade_xml_get_widget (glade_xml, "vendor_label")), manufacturer); ++ gtk_label_set_text (GTK_LABEL (glade_xml_get_widget (glade_xml, "model_label")), model); ++ gtk_label_set_text (GTK_LABEL (glade_xml_get_widget (glade_xml, "version_label")), version); ++ ++ g_free (manufacturer); ++ g_free (model); ++ g_free (version); ++ } ++} ++ ++static void ++get_property_done (DBusGProxy *proxy, DBusGProxyCall *call_id, gpointer user_data) ++{ ++ GValue value = { 0, }; ++ GError *error = NULL; ++ ++ if (!dbus_g_proxy_end_call (proxy, call_id, &error, G_TYPE_VALUE, &value, G_TYPE_INVALID)) { ++ g_warning ("%s", error->message); ++ g_error_free (error); ++ } else { ++ gtk_label_set_text (GTK_LABEL (user_data), g_value_get_string (&value)); ++ g_value_unset (&value); ++ } ++} ++ ++static gboolean ++get_info (gpointer data) ++{ ++ AppData *app_data = (AppData *) data; ++ ++ dbus_g_proxy_set_interface (app_data->proxy, MM_DBUS_INTERFACE_MODEM_GSM_CARD); ++ dbus_g_proxy_begin_call (app_data->proxy, "GetImsi", get_str_done, ++ glade_xml_get_widget (app_data->glade_xml, "imsi_label"), NULL, ++ G_TYPE_INVALID); ++ ++ dbus_g_proxy_begin_call (app_data->proxy, "GetImei", get_str_done, ++ glade_xml_get_widget (app_data->glade_xml, "imei_label"), NULL, ++ G_TYPE_INVALID); ++ ++ dbus_g_proxy_begin_call (app_data->proxy, "GetInfo", get_card_info_done, ++ app_data->glade_xml, NULL, ++ G_TYPE_INVALID); ++ ++ dbus_g_proxy_set_interface (app_data->proxy, "org.freedesktop.DBus.Properties"); ++ ++ dbus_g_proxy_begin_call (app_data->proxy, "Get", get_property_done, ++ glade_xml_get_widget (app_data->glade_xml, "driver_label"), NULL, ++ G_TYPE_STRING, MM_DBUS_INTERFACE_MODEM, ++ G_TYPE_STRING, "Driver", ++ G_TYPE_INVALID); ++ ++ dbus_g_proxy_begin_call (app_data->proxy, "Get", get_property_done, ++ glade_xml_get_widget (app_data->glade_xml, "device_label"), NULL, ++ G_TYPE_STRING, MM_DBUS_INTERFACE_MODEM, ++ G_TYPE_STRING, "DataDevice", ++ G_TYPE_INVALID); ++ ++ return FALSE; ++} ++ ++static void ++got_signal_quality (DBusGProxy *proxy, DBusGProxyCall *call_id, gpointer user_data) ++{ ++ guint32 quality = 0; ++ GError *error = NULL; ++ ++ if (!dbus_g_proxy_end_call (proxy, call_id, &error, G_TYPE_UINT, &quality, G_TYPE_INVALID)) { ++ g_warning ("%s", error->message); ++ g_error_free (error); ++ } else { ++ char *tmp; ++ ++ tmp = g_strdup_printf ("%d%%", quality); ++ gtk_label_set_text (GTK_LABEL (user_data), tmp); ++ g_free (tmp); ++ } ++} ++ ++static void ++signal_quality_changed (DBusGProxy *proxy, ++ guint32 signal_quality, ++ gpointer user_data) ++{ ++ char *tmp; ++ ++ tmp = g_strdup_printf ("%d%%", signal_quality); ++ gtk_label_set_text (GTK_LABEL (user_data), tmp); ++ g_free (tmp); ++} ++ ++static gboolean ++monitor_signal_quality (gpointer data) ++{ ++ AppData *app_data = (AppData *) data; ++ GtkWidget *label; ++ ++ label = glade_xml_get_widget (app_data->glade_xml, "signal_quality_label"); ++ ++ dbus_g_proxy_add_signal (app_data->gsm_net_proxy, "SignalQuality", G_TYPE_UINT, G_TYPE_INVALID); ++ dbus_g_proxy_connect_signal (app_data->gsm_net_proxy, "SignalQuality", ++ G_CALLBACK (signal_quality_changed), ++ label, NULL); ++ ++ dbus_g_proxy_begin_call (app_data->gsm_net_proxy, "GetSignalQuality", ++ got_signal_quality, label, NULL, G_TYPE_INVALID); ++ ++ return FALSE; ++} ++ ++static void ++got_scan_results (DBusGProxy *proxy, DBusGProxyCall *call_id, gpointer user_data) ++{ ++ AppData *app_data = (AppData *) user_data; ++ GPtrArray *array = NULL; ++ GError *error = NULL; ++ GType type; ++ ++ type = dbus_g_type_get_collection ("GPtrArray", DBUS_TYPE_G_STRING_STRING_HASHTABLE); ++ ++ if (!dbus_g_proxy_end_call (proxy, call_id, &error, type, &array, G_TYPE_INVALID)) { ++ g_warning ("Couldn't scan: %s", error->message); ++ g_error_free (error); ++ } else { ++ GtkTreeIter iter; ++ int i; ++ ++ for (i = 0; i < array->len; i++) { ++ GHashTable *hash = (GHashTable *) g_ptr_array_index (array, i); ++ char *status; ++ const char *status_str; ++ ++ status = g_hash_table_lookup (hash, "status"); ++ if (!strcmp (status, "1")) ++ status_str = "Available"; ++ else if (!strcmp (status, "2")) ++ status_str = "Current"; ++ else if (!strcmp (status, "3")) ++ status_str = "Forbidden"; ++ else ++ status_str = "Unknown"; ++ ++ gtk_list_store_append (app_data->network_store, &iter); ++ gtk_list_store_set (app_data->network_store, &iter, ++ SCAN_COL_NAME, g_hash_table_lookup (hash, "operator-long"), ++ SCAN_COL_STATUS, status_str, ++ SCAN_COL_OPER_ID, g_hash_table_lookup (hash, "operator-num"), ++ -1); ++ ++ g_hash_table_destroy (hash); ++ } ++ ++ g_ptr_array_free (array, TRUE); ++ } ++ ++ gtk_widget_hide (app_data->scan_dialog); ++ gtk_widget_set_sensitive (app_data->scan_button, TRUE); ++} ++ ++static gboolean ++scan_pulse (gpointer data) ++{ ++ GtkProgressBar *bar = GTK_PROGRESS_BAR (data); ++ ++ gtk_progress_bar_pulse (bar); ++ return gdk_window_is_visible (gtk_widget_get_parent_window (GTK_WIDGET (bar))); ++} ++ ++static void ++scan (GtkButton *button, gpointer user_data) ++{ ++ AppData *app_data = (AppData *) user_data; ++ ++ dbus_g_proxy_begin_call_with_timeout (app_data->gsm_net_proxy, "Scan", got_scan_results, ++ app_data, NULL, 120000, G_TYPE_INVALID); ++ ++ gtk_widget_set_sensitive (app_data->scan_button, FALSE); ++ gtk_list_store_clear (app_data->network_store); ++ ++ g_timeout_add (200, scan_pulse, app_data->scan_progress_bar); ++ gtk_widget_show (app_data->scan_dialog); ++} ++ ++static void ++modem_enabled (DBusGProxy *proxy, DBusGProxyCall *call_id, gpointer user_data) ++{ ++ AppData *app_data = (AppData *) user_data; ++ GError *error = NULL; ++ ++ if (!dbus_g_proxy_end_call (proxy, call_id, &error, G_TYPE_INVALID)) { ++ g_warning ("Couldn't enable modem: %s", error->message); ++ g_error_free (error); ++ g_main_loop_quit (app_data->main_loop); ++ return; ++ } ++ ++ g_idle_add (get_info, app_data); ++ g_idle_add (monitor_signal_quality, app_data); ++} ++ ++static void ++modem_enable (AppData *app_data) ++{ ++ dbus_g_proxy_begin_call (app_data->proxy, "Enable", modem_enabled, ++ app_data, NULL, ++ G_TYPE_BOOLEAN, TRUE, G_TYPE_INVALID); ++} ++ ++static void ++create_network (const char *name, const char *oper_code) ++{ ++ NMConnection *connection; ++ NMSettingGsm *s_gsm; ++ NMSettingSerial *s_serial; ++ NMSettingPPP *s_ppp; ++ NMSettingConnection *s_con; ++ GConfClient *gconf_client; ++ char *uuid; ++ ++ connection = nm_connection_new (); ++ ++ s_gsm = NM_SETTING_GSM (nm_setting_gsm_new ()); ++ g_object_set (s_gsm, ++ NM_SETTING_GSM_NUMBER, "*99#", ++ NM_SETTING_GSM_NETWORK_ID, oper_code, ++ NULL); ++ ++ nm_connection_add_setting (connection, NM_SETTING (s_gsm)); ++ ++ /* Serial setting */ ++ s_serial = (NMSettingSerial *) nm_setting_serial_new (); ++ g_object_set (s_serial, ++ NM_SETTING_SERIAL_BAUD, 115200, ++ NM_SETTING_SERIAL_BITS, 8, ++ NM_SETTING_SERIAL_PARITY, 'n', ++ NM_SETTING_SERIAL_STOPBITS, 1, ++ NULL); ++ ++ nm_connection_add_setting (connection, NM_SETTING (s_serial)); ++ ++ s_ppp = (NMSettingPPP *) nm_setting_ppp_new (); ++ nm_connection_add_setting (connection, NM_SETTING (s_ppp)); ++ ++ s_con = NM_SETTING_CONNECTION (nm_setting_connection_new ()); ++ uuid = nm_utils_uuid_generate (); ++ g_object_set (s_con, ++ NM_SETTING_CONNECTION_ID, name, ++ NM_SETTING_CONNECTION_TYPE, nm_setting_get_name (NM_SETTING (s_gsm)), ++ NM_SETTING_CONNECTION_AUTOCONNECT, FALSE, ++ NM_SETTING_CONNECTION_UUID, uuid, ++ NULL); ++ ++ g_free (uuid); ++ nm_connection_add_setting (connection, NM_SETTING (s_con)); ++ ++ gconf_client = gconf_client_get_default (); ++ if (gconf_client) { ++ char *dir = NULL; ++ int i; ++ ++ /* Find free GConf directory */ ++ for (i = 0; i < G_MAXUINT32; i++) { ++ char buf[255]; ++ ++ snprintf (&buf[0], 255, GCONF_PATH_CONNECTIONS"/%d", i); ++ if (!gconf_client_dir_exists (gconf_client, buf, NULL)) { ++ dir = g_strdup (buf); ++ break; ++ } ++ } ++ ++ nm_gconf_write_connection (connection, gconf_client, dir); ++ gconf_client_notify (gconf_client, dir); ++ gconf_client_suggest_sync (gconf_client, NULL); ++ g_free (dir); ++ g_object_unref (gconf_client); ++ } else ++ g_warning ("Writing conneciton failed"); ++ ++ g_object_unref (connection); ++} ++ ++static void ++create_network_clicked (GtkButton *button, gpointer user_data) ++{ ++ AppData *app_data = (AppData *) user_data; ++ GtkTreeSelection *selection; ++ GList *selected_rows; ++ GtkTreeModel *model = NULL; ++ GtkTreeIter iter; ++ ++ selection = gtk_tree_view_get_selection (app_data->network_list); ++ selected_rows = gtk_tree_selection_get_selected_rows (selection, &model); ++ if (!selected_rows) ++ return; ++ ++ if (gtk_tree_model_get_iter (model, &iter, (GtkTreePath *) selected_rows->data)) { ++ char *oper_name = NULL; ++ char *oper_id = NULL; ++ gint result; ++ ++ gtk_tree_model_get (model, &iter, SCAN_COL_NAME, &oper_name, -1); ++ gtk_tree_model_get (model, &iter, SCAN_COL_OPER_ID, &oper_id, -1); ++ ++ gtk_entry_set_text (app_data->create_network_name, oper_name); ++ gtk_editable_select_region (GTK_EDITABLE (app_data->create_network_name), 0, -1); ++ gtk_widget_grab_focus (GTK_WIDGET (app_data->create_network_name)); ++ ++ result = gtk_dialog_run (app_data->create_network_dialog); ++ gtk_widget_hide (GTK_WIDGET (app_data->create_network_dialog)); ++ ++ if (result == GTK_RESPONSE_OK) ++ create_network (gtk_entry_get_text (app_data->create_network_name), oper_id); ++ ++ g_free (oper_name); ++ g_free (oper_id); ++ } ++ ++ /* free memory */ ++ g_list_foreach (selected_rows, (GFunc) gtk_tree_path_free, NULL); ++ g_list_free (selected_rows); ++} ++ ++static void ++network_list_selection_changed (GtkTreeSelection *selection, gpointer user_data) ++{ ++ AppData *app_data = (AppData *) user_data; ++ GtkTreeIter iter; ++ GtkTreeModel *model; ++ ++ if (gtk_tree_selection_get_selected (selection, &model, &iter)) ++ gtk_widget_set_sensitive (app_data->create_net_button, TRUE); ++ else ++ gtk_widget_set_sensitive (app_data->create_net_button, FALSE); ++} ++ ++ ++static void ++app_data_destroy (AppData *app_data) ++{ ++ if (app_data->bus) ++ dbus_g_connection_unref (app_data->bus); ++ ++ if (app_data->proxy) ++ g_object_unref (app_data->proxy); ++ ++ if (app_data->gsm_net_proxy) ++ g_object_unref (app_data->gsm_net_proxy); ++ ++ if (app_data->glade_xml) ++ g_object_unref (app_data->glade_xml); ++ ++ if (app_data->main_loop) ++ g_main_loop_unref (app_data->main_loop); ++ ++ g_slice_free (AppData, app_data); ++} ++ ++static void ++close_cb (GtkDialog *dialog, ++ gint response_id, ++ gpointer user_data) ++{ ++ AppData *app_data = (AppData *) user_data; ++ ++ dbus_g_proxy_set_interface (app_data->proxy, MM_DBUS_INTERFACE_MODEM); ++ dbus_g_proxy_call_no_reply (app_data->proxy, "Enable", G_TYPE_BOOLEAN, FALSE, G_TYPE_INVALID); ++ ++ g_main_loop_quit (app_data->main_loop); ++ app_data_destroy (app_data); ++} ++ ++ ++static GtkListStore * ++prepare_network_list (GtkTreeView *treeview) ++{ ++ GtkListStore *store; ++ ++ store = gtk_list_store_new (3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING); ++ gtk_tree_view_set_model (treeview, GTK_TREE_MODEL (store)); ++ g_object_unref (store); ++ ++ gtk_tree_view_insert_column_with_attributes (treeview, ++ -1, "Name", gtk_cell_renderer_text_new (), ++ "text", SCAN_COL_NAME, ++ NULL); ++ ++ gtk_tree_view_insert_column_with_attributes (treeview, ++ -1, "Status", gtk_cell_renderer_text_new (), ++ "text", SCAN_COL_STATUS, ++ NULL); ++ ++ return store; ++} ++ ++static AppData * ++app_data_create (const char *udi) ++{ ++ AppData *app_data; ++ GtkTreeSelection *selection; ++ GError *error = NULL; ++ ++ app_data = g_slice_new0 (AppData); ++ ++ /* DBus */ ++ app_data->bus = dbus_g_bus_get (DBUS_BUS_SYSTEM, &error); ++ if (!app_data->bus) { ++ g_error ("Couldn't connect to DBus: %s", error->message); ++ g_error_free (error); ++ g_slice_free (AppData, app_data); ++ ++ return NULL; ++ } ++ ++ app_data->proxy = dbus_g_proxy_new_for_name (app_data->bus, MM_DBUS_SERVICE, udi, MM_DBUS_INTERFACE_MODEM); ++ app_data->gsm_net_proxy = dbus_g_proxy_new_from_proxy (app_data->proxy, MM_DBUS_INTERFACE_MODEM_GSM_NETWORK, NULL); ++ ++ /* UI */ ++ app_data->glade_xml = glade_xml_new (GLADEDIR "/nm-modem-properties.glade", NULL, NULL); ++ if (!app_data->glade_xml) { ++ g_error ("Could not load Glade file"); ++ g_slice_free (AppData, app_data); ++ ++ return NULL; ++ } ++ ++ app_data->main_dialog = GTK_DIALOG (glade_xml_get_widget (app_data->glade_xml, "main_dialog")); ++ g_signal_connect (app_data->main_dialog, "response", G_CALLBACK (close_cb), app_data); ++ ++ app_data->network_list = GTK_TREE_VIEW (glade_xml_get_widget (app_data->glade_xml, "network_list")); ++ app_data->network_store = prepare_network_list (app_data->network_list); ++ app_data->scan_button = glade_xml_get_widget (app_data->glade_xml, "scan_button"); ++ g_signal_connect (app_data->scan_button, "clicked", G_CALLBACK (scan), app_data); ++ ++ app_data->scan_dialog = glade_xml_get_widget (app_data->glade_xml, "scan_dialog"); ++ gtk_window_set_transient_for (GTK_WINDOW (app_data->scan_dialog), GTK_WINDOW (app_data->main_dialog)); ++ ++ app_data->scan_progress_bar = GTK_PROGRESS_BAR (glade_xml_get_widget (app_data->glade_xml, "scan_progress_bar")); ++ ++ app_data->create_net_button = glade_xml_get_widget (app_data->glade_xml, "create_connection_button"); ++ gtk_widget_set_sensitive (app_data->create_net_button, FALSE); ++ g_signal_connect (app_data->create_net_button, "clicked", G_CALLBACK (create_network_clicked), app_data); ++ selection = gtk_tree_view_get_selection (app_data->network_list); ++ g_signal_connect (selection, "changed", G_CALLBACK (network_list_selection_changed), app_data); ++ ++ app_data->create_network_dialog = GTK_DIALOG (glade_xml_get_widget (app_data->glade_xml, "create_network_dialog")); ++ app_data->create_network_name = GTK_ENTRY (glade_xml_get_widget (app_data->glade_xml, "create_network_name")); ++ ++ app_data->main_loop = g_main_loop_new (NULL, FALSE); ++ ++ return app_data; ++} ++ ++int ++main (int argc, char *argv[]) ++{ ++ //const char *udi = "/org/freedesktop/Hal/devices/usb_device_12d1_1003_noserial_if0_serial_usb_0"; ++ AppData *app_data; ++ ++ if (argc != 2) { ++ g_print ("Usage: %s <udi>\n", argv[0]); ++ return 1; ++ } ++ ++ gtk_init (&argc, &argv); ++ ++ app_data = app_data_create (argv[1]); ++ if (app_data) { ++ modem_enable (app_data); ++ g_main_loop_run (app_data->main_loop); ++ } ++ ++ return 0; ++} +diff --git a/src/modems/nm-modem-properties.glade b/src/modems/nm-modem-properties.glade +new file mode 100644 +index 0000000..5c1285c +--- /dev/null ++++ b/src/modems/nm-modem-properties.glade +@@ -0,0 +1,1083 @@ ++<?xml version="1.0" standalone="no"?> <!--*- mode: xml -*--> ++<!DOCTYPE glade-interface SYSTEM "http://glade.gnome.org/glade-2.0.dtd"> ++ ++<glade-interface> ++ ++<widget class="GtkDialog" id="main_dialog"> ++ <property name="visible">True</property> ++ <property name="title" translatable="yes">GSM modem properties</property> ++ <property name="type">GTK_WINDOW_TOPLEVEL</property> ++ <property name="window_position">GTK_WIN_POS_NONE</property> ++ <property name="modal">False</property> ++ <property name="resizable">True</property> ++ <property name="destroy_with_parent">False</property> ++ <property name="decorated">True</property> ++ <property name="skip_taskbar_hint">False</property> ++ <property name="skip_pager_hint">False</property> ++ <property name="type_hint">GDK_WINDOW_TYPE_HINT_DIALOG</property> ++ <property name="gravity">GDK_GRAVITY_NORTH_WEST</property> ++ <property name="focus_on_map">True</property> ++ <property name="urgency_hint">False</property> ++ <property name="has_separator">True</property> ++ ++ <child internal-child="vbox"> ++ <widget class="GtkVBox" id="dialog-vbox1"> ++ <property name="visible">True</property> ++ <property name="homogeneous">False</property> ++ <property name="spacing">0</property> ++ ++ <child internal-child="action_area"> ++ <widget class="GtkHButtonBox" id="dialog-action_area1"> ++ <property name="visible">True</property> ++ <property name="layout_style">GTK_BUTTONBOX_END</property> ++ ++ <child> ++ <widget class="GtkButton" id="closebutton1"> ++ <property name="visible">True</property> ++ <property name="can_default">True</property> ++ <property name="can_focus">True</property> ++ <property name="label">gtk-close</property> ++ <property name="use_stock">True</property> ++ <property name="relief">GTK_RELIEF_NORMAL</property> ++ <property name="focus_on_click">True</property> ++ <property name="response_id">-7</property> ++ </widget> ++ </child> ++ </widget> ++ <packing> ++ <property name="padding">0</property> ++ <property name="expand">False</property> ++ <property name="fill">True</property> ++ <property name="pack_type">GTK_PACK_END</property> ++ </packing> ++ </child> ++ ++ <child> ++ <widget class="GtkVBox" id="vbox1"> ++ <property name="visible">True</property> ++ <property name="homogeneous">False</property> ++ <property name="spacing">6</property> ++ ++ <child> ++ <widget class="GtkHBox" id="hbox2"> ++ <property name="visible">True</property> ++ <property name="homogeneous">False</property> ++ <property name="spacing">6</property> ++ ++ <child> ++ <widget class="GtkFrame" id="frame1"> ++ <property name="visible">True</property> ++ <property name="label_xalign">0</property> ++ <property name="label_yalign">0.5</property> ++ <property name="shadow_type">GTK_SHADOW_NONE</property> ++ ++ <child> ++ <widget class="GtkAlignment" id="alignment1"> ++ <property name="visible">True</property> ++ <property name="xalign">0.5</property> ++ <property name="yalign">0.5</property> ++ <property name="xscale">1</property> ++ <property name="yscale">1</property> ++ <property name="top_padding">0</property> ++ <property name="bottom_padding">0</property> ++ <property name="left_padding">12</property> ++ <property name="right_padding">0</property> ++ ++ <child> ++ <widget class="GtkTable" id="info_table"> ++ <property name="visible">True</property> ++ <property name="n_rows">8</property> ++ <property name="n_columns">2</property> ++ <property name="homogeneous">False</property> ++ <property name="row_spacing">0</property> ++ <property name="column_spacing">6</property> ++ ++ <child> ++ <widget class="GtkLabel" id="label6"> ++ <property name="visible">True</property> ++ <property name="label" translatable="yes">Vendor</property> ++ <property name="use_underline">False</property> ++ <property name="use_markup">False</property> ++ <property name="justify">GTK_JUSTIFY_LEFT</property> ++ <property name="wrap">False</property> ++ <property name="selectable">False</property> ++ <property name="xalign">0</property> ++ <property name="yalign">0</property> ++ <property name="xpad">0</property> ++ <property name="ypad">0</property> ++ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> ++ <property name="width_chars">-1</property> ++ <property name="single_line_mode">False</property> ++ <property name="angle">0</property> ++ </widget> ++ <packing> ++ <property name="left_attach">0</property> ++ <property name="right_attach">1</property> ++ <property name="top_attach">0</property> ++ <property name="bottom_attach">1</property> ++ <property name="x_options">fill</property> ++ <property name="y_options"></property> ++ </packing> ++ </child> ++ ++ <child> ++ <widget class="GtkLabel" id="label7"> ++ <property name="visible">True</property> ++ <property name="label" translatable="yes">Model</property> ++ <property name="use_underline">False</property> ++ <property name="use_markup">False</property> ++ <property name="justify">GTK_JUSTIFY_LEFT</property> ++ <property name="wrap">False</property> ++ <property name="selectable">False</property> ++ <property name="xalign">0</property> ++ <property name="yalign">0</property> ++ <property name="xpad">0</property> ++ <property name="ypad">0</property> ++ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> ++ <property name="width_chars">-1</property> ++ <property name="single_line_mode">False</property> ++ <property name="angle">0</property> ++ </widget> ++ <packing> ++ <property name="left_attach">0</property> ++ <property name="right_attach">1</property> ++ <property name="top_attach">1</property> ++ <property name="bottom_attach">2</property> ++ <property name="x_options">fill</property> ++ <property name="y_options"></property> ++ </packing> ++ </child> ++ ++ <child> ++ <widget class="GtkLabel" id="label8"> ++ <property name="visible">True</property> ++ <property name="label" translatable="yes">Version</property> ++ <property name="use_underline">False</property> ++ <property name="use_markup">False</property> ++ <property name="justify">GTK_JUSTIFY_LEFT</property> ++ <property name="wrap">False</property> ++ <property name="selectable">False</property> ++ <property name="xalign">0</property> ++ <property name="yalign">0</property> ++ <property name="xpad">0</property> ++ <property name="ypad">0</property> ++ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> ++ <property name="width_chars">-1</property> ++ <property name="single_line_mode">False</property> ++ <property name="angle">0</property> ++ </widget> ++ <packing> ++ <property name="left_attach">0</property> ++ <property name="right_attach">1</property> ++ <property name="top_attach">2</property> ++ <property name="bottom_attach">3</property> ++ <property name="x_options">fill</property> ++ <property name="y_options"></property> ++ </packing> ++ </child> ++ ++ <child> ++ <widget class="GtkLabel" id="label9"> ++ <property name="visible">True</property> ++ <property name="label" translatable="yes">Driver</property> ++ <property name="use_underline">False</property> ++ <property name="use_markup">False</property> ++ <property name="justify">GTK_JUSTIFY_LEFT</property> ++ <property name="wrap">False</property> ++ <property name="selectable">False</property> ++ <property name="xalign">0</property> ++ <property name="yalign">0</property> ++ <property name="xpad">0</property> ++ <property name="ypad">0</property> ++ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> ++ <property name="width_chars">-1</property> ++ <property name="single_line_mode">False</property> ++ <property name="angle">0</property> ++ </widget> ++ <packing> ++ <property name="left_attach">0</property> ++ <property name="right_attach">1</property> ++ <property name="top_attach">3</property> ++ <property name="bottom_attach">4</property> ++ <property name="x_options">fill</property> ++ <property name="y_options"></property> ++ </packing> ++ </child> ++ ++ <child> ++ <widget class="GtkLabel" id="label10"> ++ <property name="visible">True</property> ++ <property name="label" translatable="yes">Data device</property> ++ <property name="use_underline">False</property> ++ <property name="use_markup">False</property> ++ <property name="justify">GTK_JUSTIFY_LEFT</property> ++ <property name="wrap">False</property> ++ <property name="selectable">False</property> ++ <property name="xalign">0</property> ++ <property name="yalign">0</property> ++ <property name="xpad">0</property> ++ <property name="ypad">0</property> ++ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> ++ <property name="width_chars">-1</property> ++ <property name="single_line_mode">False</property> ++ <property name="angle">0</property> ++ </widget> ++ <packing> ++ <property name="left_attach">0</property> ++ <property name="right_attach">1</property> ++ <property name="top_attach">4</property> ++ <property name="bottom_attach">5</property> ++ <property name="x_options">fill</property> ++ <property name="y_options"></property> ++ </packing> ++ </child> ++ ++ <child> ++ <widget class="GtkLabel" id="label11"> ++ <property name="visible">True</property> ++ <property name="label" translatable="yes">IMSI</property> ++ <property name="use_underline">False</property> ++ <property name="use_markup">False</property> ++ <property name="justify">GTK_JUSTIFY_LEFT</property> ++ <property name="wrap">False</property> ++ <property name="selectable">False</property> ++ <property name="xalign">0</property> ++ <property name="yalign">0</property> ++ <property name="xpad">0</property> ++ <property name="ypad">0</property> ++ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> ++ <property name="width_chars">-1</property> ++ <property name="single_line_mode">False</property> ++ <property name="angle">0</property> ++ </widget> ++ <packing> ++ <property name="left_attach">0</property> ++ <property name="right_attach">1</property> ++ <property name="top_attach">5</property> ++ <property name="bottom_attach">6</property> ++ <property name="x_options">fill</property> ++ <property name="y_options"></property> ++ </packing> ++ </child> ++ ++ <child> ++ <widget class="GtkLabel" id="label12"> ++ <property name="visible">True</property> ++ <property name="label" translatable="yes">IMEI</property> ++ <property name="use_underline">False</property> ++ <property name="use_markup">False</property> ++ <property name="justify">GTK_JUSTIFY_LEFT</property> ++ <property name="wrap">False</property> ++ <property name="selectable">False</property> ++ <property name="xalign">0</property> ++ <property name="yalign">0</property> ++ <property name="xpad">0</property> ++ <property name="ypad">0</property> ++ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> ++ <property name="width_chars">-1</property> ++ <property name="single_line_mode">False</property> ++ <property name="angle">0</property> ++ </widget> ++ <packing> ++ <property name="left_attach">0</property> ++ <property name="right_attach">1</property> ++ <property name="top_attach">6</property> ++ <property name="bottom_attach">7</property> ++ <property name="x_options">fill</property> ++ <property name="y_options"></property> ++ </packing> ++ </child> ++ ++ <child> ++ <widget class="GtkLabel" id="label13"> ++ <property name="visible">True</property> ++ <property name="label" translatable="yes">Signal quality</property> ++ <property name="use_underline">False</property> ++ <property name="use_markup">False</property> ++ <property name="justify">GTK_JUSTIFY_LEFT</property> ++ <property name="wrap">False</property> ++ <property name="selectable">False</property> ++ <property name="xalign">0</property> ++ <property name="yalign">0</property> ++ <property name="xpad">0</property> ++ <property name="ypad">0</property> ++ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> ++ <property name="width_chars">-1</property> ++ <property name="single_line_mode">False</property> ++ <property name="angle">0</property> ++ </widget> ++ <packing> ++ <property name="left_attach">0</property> ++ <property name="right_attach">1</property> ++ <property name="top_attach">7</property> ++ <property name="bottom_attach">8</property> ++ <property name="x_options">fill</property> ++ <property name="y_options"></property> ++ </packing> ++ </child> ++ ++ <child> ++ <widget class="GtkLabel" id="vendor_label"> ++ <property name="visible">True</property> ++ <property name="label" translatable="yes"></property> ++ <property name="use_underline">False</property> ++ <property name="use_markup">False</property> ++ <property name="justify">GTK_JUSTIFY_LEFT</property> ++ <property name="wrap">False</property> ++ <property name="selectable">False</property> ++ <property name="xalign">0</property> ++ <property name="yalign">0</property> ++ <property name="xpad">0</property> ++ <property name="ypad">0</property> ++ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> ++ <property name="width_chars">-1</property> ++ <property name="single_line_mode">False</property> ++ <property name="angle">0</property> ++ </widget> ++ <packing> ++ <property name="left_attach">1</property> ++ <property name="right_attach">2</property> ++ <property name="top_attach">0</property> ++ <property name="bottom_attach">1</property> ++ <property name="x_options">fill</property> ++ <property name="y_options"></property> ++ </packing> ++ </child> ++ ++ <child> ++ <widget class="GtkLabel" id="model_label"> ++ <property name="visible">True</property> ++ <property name="label" translatable="yes"></property> ++ <property name="use_underline">False</property> ++ <property name="use_markup">False</property> ++ <property name="justify">GTK_JUSTIFY_LEFT</property> ++ <property name="wrap">False</property> ++ <property name="selectable">False</property> ++ <property name="xalign">0</property> ++ <property name="yalign">0</property> ++ <property name="xpad">0</property> ++ <property name="ypad">0</property> ++ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> ++ <property name="width_chars">-1</property> ++ <property name="single_line_mode">False</property> ++ <property name="angle">0</property> ++ </widget> ++ <packing> ++ <property name="left_attach">1</property> ++ <property name="right_attach">2</property> ++ <property name="top_attach">1</property> ++ <property name="bottom_attach">2</property> ++ <property name="x_options">fill</property> ++ <property name="y_options"></property> ++ </packing> ++ </child> ++ ++ <child> ++ <widget class="GtkLabel" id="version_label"> ++ <property name="visible">True</property> ++ <property name="label" translatable="yes"></property> ++ <property name="use_underline">False</property> ++ <property name="use_markup">False</property> ++ <property name="justify">GTK_JUSTIFY_LEFT</property> ++ <property name="wrap">False</property> ++ <property name="selectable">False</property> ++ <property name="xalign">0</property> ++ <property name="yalign">0</property> ++ <property name="xpad">0</property> ++ <property name="ypad">0</property> ++ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> ++ <property name="width_chars">-1</property> ++ <property name="single_line_mode">False</property> ++ <property name="angle">0</property> ++ </widget> ++ <packing> ++ <property name="left_attach">1</property> ++ <property name="right_attach">2</property> ++ <property name="top_attach">2</property> ++ <property name="bottom_attach">3</property> ++ <property name="x_options">fill</property> ++ <property name="y_options"></property> ++ </packing> ++ </child> ++ ++ <child> ++ <widget class="GtkLabel" id="driver_label"> ++ <property name="visible">True</property> ++ <property name="label" translatable="yes"></property> ++ <property name="use_underline">False</property> ++ <property name="use_markup">False</property> ++ <property name="justify">GTK_JUSTIFY_LEFT</property> ++ <property name="wrap">False</property> ++ <property name="selectable">False</property> ++ <property name="xalign">0</property> ++ <property name="yalign">0</property> ++ <property name="xpad">0</property> ++ <property name="ypad">0</property> ++ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> ++ <property name="width_chars">-1</property> ++ <property name="single_line_mode">False</property> ++ <property name="angle">0</property> ++ </widget> ++ <packing> ++ <property name="left_attach">1</property> ++ <property name="right_attach">2</property> ++ <property name="top_attach">3</property> ++ <property name="bottom_attach">4</property> ++ <property name="x_options">fill</property> ++ <property name="y_options"></property> ++ </packing> ++ </child> ++ ++ <child> ++ <widget class="GtkLabel" id="device_label"> ++ <property name="visible">True</property> ++ <property name="label" translatable="yes"></property> ++ <property name="use_underline">False</property> ++ <property name="use_markup">False</property> ++ <property name="justify">GTK_JUSTIFY_LEFT</property> ++ <property name="wrap">False</property> ++ <property name="selectable">False</property> ++ <property name="xalign">0</property> ++ <property name="yalign">0</property> ++ <property name="xpad">0</property> ++ <property name="ypad">0</property> ++ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> ++ <property name="width_chars">-1</property> ++ <property name="single_line_mode">False</property> ++ <property name="angle">0</property> ++ </widget> ++ <packing> ++ <property name="left_attach">1</property> ++ <property name="right_attach">2</property> ++ <property name="top_attach">4</property> ++ <property name="bottom_attach">5</property> ++ <property name="x_options">fill</property> ++ <property name="y_options"></property> ++ </packing> ++ </child> ++ ++ <child> ++ <widget class="GtkLabel" id="imsi_label"> ++ <property name="visible">True</property> ++ <property name="label" translatable="yes"></property> ++ <property name="use_underline">False</property> ++ <property name="use_markup">False</property> ++ <property name="justify">GTK_JUSTIFY_LEFT</property> ++ <property name="wrap">False</property> ++ <property name="selectable">False</property> ++ <property name="xalign">0</property> ++ <property name="yalign">0</property> ++ <property name="xpad">0</property> ++ <property name="ypad">0</property> ++ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> ++ <property name="width_chars">-1</property> ++ <property name="single_line_mode">False</property> ++ <property name="angle">0</property> ++ </widget> ++ <packing> ++ <property name="left_attach">1</property> ++ <property name="right_attach">2</property> ++ <property name="top_attach">5</property> ++ <property name="bottom_attach">6</property> ++ <property name="x_options">fill</property> ++ <property name="y_options"></property> ++ </packing> ++ </child> ++ ++ <child> ++ <widget class="GtkLabel" id="imei_label"> ++ <property name="visible">True</property> ++ <property name="label" translatable="yes"></property> ++ <property name="use_underline">False</property> ++ <property name="use_markup">False</property> ++ <property name="justify">GTK_JUSTIFY_LEFT</property> ++ <property name="wrap">False</property> ++ <property name="selectable">False</property> ++ <property name="xalign">0</property> ++ <property name="yalign">0</property> ++ <property name="xpad">0</property> ++ <property name="ypad">0</property> ++ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> ++ <property name="width_chars">-1</property> ++ <property name="single_line_mode">False</property> ++ <property name="angle">0</property> ++ </widget> ++ <packing> ++ <property name="left_attach">1</property> ++ <property name="right_attach">2</property> ++ <property name="top_attach">6</property> ++ <property name="bottom_attach">7</property> ++ <property name="x_options">fill</property> ++ <property name="y_options"></property> ++ </packing> ++ </child> ++ ++ <child> ++ <widget class="GtkLabel" id="signal_quality_label"> ++ <property name="visible">True</property> ++ <property name="label" translatable="yes"></property> ++ <property name="use_underline">False</property> ++ <property name="use_markup">False</property> ++ <property name="justify">GTK_JUSTIFY_LEFT</property> ++ <property name="wrap">False</property> ++ <property name="selectable">False</property> ++ <property name="xalign">0</property> ++ <property name="yalign">0</property> ++ <property name="xpad">0</property> ++ <property name="ypad">0</property> ++ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> ++ <property name="width_chars">-1</property> ++ <property name="single_line_mode">False</property> ++ <property name="angle">0</property> ++ </widget> ++ <packing> ++ <property name="left_attach">1</property> ++ <property name="right_attach">2</property> ++ <property name="top_attach">7</property> ++ <property name="bottom_attach">8</property> ++ <property name="x_options">fill</property> ++ <property name="y_options"></property> ++ </packing> ++ </child> ++ </widget> ++ </child> ++ </widget> ++ </child> ++ ++ <child> ++ <widget class="GtkLabel" id="label1"> ++ <property name="visible">True</property> ++ <property name="label" translatable="yes"><b>Information</b></property> ++ <property name="use_underline">False</property> ++ <property name="use_markup">True</property> ++ <property name="justify">GTK_JUSTIFY_LEFT</property> ++ <property name="wrap">False</property> ++ <property name="selectable">False</property> ++ <property name="xalign">0.5</property> ++ <property name="yalign">0.5</property> ++ <property name="xpad">0</property> ++ <property name="ypad">0</property> ++ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> ++ <property name="width_chars">-1</property> ++ <property name="single_line_mode">False</property> ++ <property name="angle">0</property> ++ </widget> ++ <packing> ++ <property name="type">label_item</property> ++ </packing> ++ </child> ++ </widget> ++ <packing> ++ <property name="padding">0</property> ++ <property name="expand">True</property> ++ <property name="fill">True</property> ++ </packing> ++ </child> ++ ++ <child> ++ <widget class="GtkFrame" id="frame3"> ++ <property name="label_xalign">0</property> ++ <property name="label_yalign">0.5</property> ++ <property name="shadow_type">GTK_SHADOW_NONE</property> ++ ++ <child> ++ <widget class="GtkAlignment" id="alignment3"> ++ <property name="visible">True</property> ++ <property name="xalign">0.5</property> ++ <property name="yalign">0.5</property> ++ <property name="xscale">1</property> ++ <property name="yscale">1</property> ++ <property name="top_padding">0</property> ++ <property name="bottom_padding">0</property> ++ <property name="left_padding">12</property> ++ <property name="right_padding">0</property> ++ ++ <child> ++ <placeholder/> ++ </child> ++ </widget> ++ </child> ++ ++ <child> ++ <widget class="GtkLabel" id="label4"> ++ <property name="visible">True</property> ++ <property name="label" translatable="yes"><b>PIN operations</b></property> ++ <property name="use_underline">False</property> ++ <property name="use_markup">True</property> ++ <property name="justify">GTK_JUSTIFY_LEFT</property> ++ <property name="wrap">False</property> ++ <property name="selectable">False</property> ++ <property name="xalign">0.5</property> ++ <property name="yalign">0.5</property> ++ <property name="xpad">0</property> ++ <property name="ypad">0</property> ++ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> ++ <property name="width_chars">-1</property> ++ <property name="single_line_mode">False</property> ++ <property name="angle">0</property> ++ </widget> ++ <packing> ++ <property name="type">label_item</property> ++ </packing> ++ </child> ++ </widget> ++ <packing> ++ <property name="padding">0</property> ++ <property name="expand">True</property> ++ <property name="fill">True</property> ++ </packing> ++ </child> ++ </widget> ++ <packing> ++ <property name="padding">0</property> ++ <property name="expand">True</property> ++ <property name="fill">True</property> ++ </packing> ++ </child> ++ ++ <child> ++ <widget class="GtkFrame" id="frame2"> ++ <property name="visible">True</property> ++ <property name="label_xalign">0</property> ++ <property name="label_yalign">0.5</property> ++ <property name="shadow_type">GTK_SHADOW_NONE</property> ++ ++ <child> ++ <widget class="GtkAlignment" id="alignment2"> ++ <property name="visible">True</property> ++ <property name="xalign">0.5</property> ++ <property name="yalign">0.5</property> ++ <property name="xscale">1</property> ++ <property name="yscale">1</property> ++ <property name="top_padding">0</property> ++ <property name="bottom_padding">0</property> ++ <property name="left_padding">12</property> ++ <property name="right_padding">0</property> ++ ++ <child> ++ <widget class="GtkHBox" id="hbox1"> ++ <property name="visible">True</property> ++ <property name="homogeneous">False</property> ++ <property name="spacing">6</property> ++ ++ <child> ++ <widget class="GtkScrolledWindow" id="scrolledwindow1"> ++ <property name="visible">True</property> ++ <property name="can_focus">True</property> ++ <property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property> ++ <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property> ++ <property name="shadow_type">GTK_SHADOW_IN</property> ++ <property name="window_placement">GTK_CORNER_TOP_LEFT</property> ++ ++ <child> ++ <widget class="GtkTreeView" id="network_list"> ++ <property name="visible">True</property> ++ <property name="can_focus">True</property> ++ <property name="headers_visible">False</property> ++ <property name="rules_hint">False</property> ++ <property name="reorderable">False</property> ++ <property name="enable_search">True</property> ++ <property name="fixed_height_mode">False</property> ++ <property name="hover_selection">False</property> ++ <property name="hover_expand">False</property> ++ </widget> ++ </child> ++ </widget> ++ <packing> ++ <property name="padding">0</property> ++ <property name="expand">True</property> ++ <property name="fill">True</property> ++ </packing> ++ </child> ++ ++ <child> ++ <widget class="GtkVButtonBox" id="vbuttonbox1"> ++ <property name="visible">True</property> ++ <property name="layout_style">GTK_BUTTONBOX_START</property> ++ <property name="spacing">0</property> ++ ++ <child> ++ <widget class="GtkButton" id="scan_button"> ++ <property name="visible">True</property> ++ <property name="can_default">True</property> ++ <property name="can_focus">True</property> ++ <property name="label" translatable="yes">Scan</property> ++ <property name="use_underline">True</property> ++ <property name="relief">GTK_RELIEF_NORMAL</property> ++ <property name="focus_on_click">True</property> ++ </widget> ++ </child> ++ ++ <child> ++ <widget class="GtkButton" id="create_connection_button"> ++ <property name="visible">True</property> ++ <property name="can_default">True</property> ++ <property name="can_focus">True</property> ++ <property name="label" translatable="yes">Create connection</property> ++ <property name="use_underline">True</property> ++ <property name="relief">GTK_RELIEF_NORMAL</property> ++ <property name="focus_on_click">True</property> ++ </widget> ++ </child> ++ </widget> ++ <packing> ++ <property name="padding">0</property> ++ <property name="expand">False</property> ++ <property name="fill">False</property> ++ </packing> ++ </child> ++ </widget> ++ </child> ++ </widget> ++ </child> ++ ++ <child> ++ <widget class="GtkLabel" id="label2"> ++ <property name="visible">True</property> ++ <property name="label" translatable="yes"><b>Networks</b></property> ++ <property name="use_underline">False</property> ++ <property name="use_markup">True</property> ++ <property name="justify">GTK_JUSTIFY_LEFT</property> ++ <property name="wrap">False</property> ++ <property name="selectable">False</property> ++ <property name="xalign">0.5</property> ++ <property name="yalign">0.5</property> ++ <property name="xpad">0</property> ++ <property name="ypad">0</property> ++ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> ++ <property name="width_chars">-1</property> ++ <property name="single_line_mode">False</property> ++ <property name="angle">0</property> ++ </widget> ++ <packing> ++ <property name="type">label_item</property> ++ </packing> ++ </child> ++ </widget> ++ <packing> ++ <property name="padding">0</property> ++ <property name="expand">True</property> ++ <property name="fill">True</property> ++ </packing> ++ </child> ++ </widget> ++ <packing> ++ <property name="padding">0</property> ++ <property name="expand">True</property> ++ <property name="fill">True</property> ++ </packing> ++ </child> ++ </widget> ++ </child> ++</widget> ++ ++<widget class="GtkDialog" id="create_network_dialog"> ++ <property name="title" translatable="yes">Create new connection</property> ++ <property name="type">GTK_WINDOW_TOPLEVEL</property> ++ <property name="window_position">GTK_WIN_POS_NONE</property> ++ <property name="modal">False</property> ++ <property name="resizable">False</property> ++ <property name="destroy_with_parent">False</property> ++ <property name="decorated">True</property> ++ <property name="skip_taskbar_hint">False</property> ++ <property name="skip_pager_hint">False</property> ++ <property name="type_hint">GDK_WINDOW_TYPE_HINT_DIALOG</property> ++ <property name="gravity">GDK_GRAVITY_NORTH_WEST</property> ++ <property name="focus_on_map">True</property> ++ <property name="urgency_hint">False</property> ++ <property name="has_separator">True</property> ++ ++ <child internal-child="vbox"> ++ <widget class="GtkVBox" id="dialog-vbox2"> ++ <property name="visible">True</property> ++ <property name="homogeneous">False</property> ++ <property name="spacing">0</property> ++ ++ <child internal-child="action_area"> ++ <widget class="GtkHButtonBox" id="dialog-action_area2"> ++ <property name="visible">True</property> ++ <property name="layout_style">GTK_BUTTONBOX_END</property> ++ ++ <child> ++ <widget class="GtkButton" id="cancelbutton1"> ++ <property name="visible">True</property> ++ <property name="can_default">True</property> ++ <property name="can_focus">True</property> ++ <property name="label">gtk-cancel</property> ++ <property name="use_stock">True</property> ++ <property name="relief">GTK_RELIEF_NORMAL</property> ++ <property name="focus_on_click">True</property> ++ <property name="response_id">-6</property> ++ </widget> ++ </child> ++ ++ <child> ++ <widget class="GtkButton" id="okbutton1"> ++ <property name="visible">True</property> ++ <property name="can_default">True</property> ++ <property name="can_focus">True</property> ++ <property name="label">gtk-ok</property> ++ <property name="use_stock">True</property> ++ <property name="relief">GTK_RELIEF_NORMAL</property> ++ <property name="focus_on_click">True</property> ++ <property name="response_id">-5</property> ++ </widget> ++ </child> ++ </widget> ++ <packing> ++ <property name="padding">0</property> ++ <property name="expand">False</property> ++ <property name="fill">True</property> ++ <property name="pack_type">GTK_PACK_END</property> ++ </packing> ++ </child> ++ ++ <child> ++ <widget class="GtkVBox" id="vbox2"> ++ <property name="border_width">6</property> ++ <property name="visible">True</property> ++ <property name="homogeneous">False</property> ++ <property name="spacing">6</property> ++ ++ <child> ++ <widget class="GtkLabel" id="label3"> ++ <property name="visible">True</property> ++ <property name="label" translatable="yes">Please choose a name for the connection</property> ++ <property name="use_underline">False</property> ++ <property name="use_markup">False</property> ++ <property name="justify">GTK_JUSTIFY_LEFT</property> ++ <property name="wrap">False</property> ++ <property name="selectable">False</property> ++ <property name="xalign">0.5</property> ++ <property name="yalign">0.5</property> ++ <property name="xpad">0</property> ++ <property name="ypad">0</property> ++ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> ++ <property name="width_chars">-1</property> ++ <property name="single_line_mode">False</property> ++ <property name="angle">0</property> ++ </widget> ++ <packing> ++ <property name="padding">0</property> ++ <property name="expand">False</property> ++ <property name="fill">False</property> ++ </packing> ++ </child> ++ ++ <child> ++ <widget class="GtkEntry" id="create_network_name"> ++ <property name="visible">True</property> ++ <property name="can_focus">True</property> ++ <property name="editable">True</property> ++ <property name="visibility">True</property> ++ <property name="max_length">0</property> ++ <property name="text" translatable="yes"></property> ++ <property name="has_frame">True</property> ++ <property name="invisible_char">●</property> ++ <property name="activates_default">True</property> ++ </widget> ++ <packing> ++ <property name="padding">0</property> ++ <property name="expand">False</property> ++ <property name="fill">False</property> ++ </packing> ++ </child> ++ </widget> ++ <packing> ++ <property name="padding">0</property> ++ <property name="expand">True</property> ++ <property name="fill">True</property> ++ </packing> ++ </child> ++ </widget> ++ </child> ++</widget> ++ ++<widget class="GtkWindow" id="scan_dialog"> ++ <property name="title" translatable="yes"></property> ++ <property name="type">GTK_WINDOW_TOPLEVEL</property> ++ <property name="window_position">GTK_WIN_POS_CENTER_ON_PARENT</property> ++ <property name="modal">True</property> ++ <property name="resizable">False</property> ++ <property name="destroy_with_parent">False</property> ++ <property name="decorated">False</property> ++ <property name="skip_taskbar_hint">True</property> ++ <property name="skip_pager_hint">True</property> ++ <property name="type_hint">GDK_WINDOW_TYPE_HINT_DIALOG</property> ++ <property name="gravity">GDK_GRAVITY_CENTER</property> ++ <property name="focus_on_map">True</property> ++ <property name="urgency_hint">False</property> ++ ++ <child> ++ <widget class="GtkVBox" id="vbox3"> ++ <property name="border_width">6</property> ++ <property name="visible">True</property> ++ <property name="homogeneous">False</property> ++ <property name="spacing">6</property> ++ ++ <child> ++ <widget class="GtkLabel" id="label5"> ++ <property name="visible">True</property> ++ <property name="label" translatable="yes">Scanning... Please wait.</property> ++ <property name="use_underline">False</property> ++ <property name="use_markup">False</property> ++ <property name="justify">GTK_JUSTIFY_LEFT</property> ++ <property name="wrap">False</property> ++ <property name="selectable">False</property> ++ <property name="xalign">0.5</property> ++ <property name="yalign">0.5</property> ++ <property name="xpad">0</property> ++ <property name="ypad">0</property> ++ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> ++ <property name="width_chars">-1</property> ++ <property name="single_line_mode">False</property> ++ <property name="angle">0</property> ++ </widget> ++ <packing> ++ <property name="padding">0</property> ++ <property name="expand">False</property> ++ <property name="fill">False</property> ++ </packing> ++ </child> ++ ++ <child> ++ <widget class="GtkProgressBar" id="scan_progress_bar"> ++ <property name="visible">True</property> ++ <property name="orientation">GTK_PROGRESS_LEFT_TO_RIGHT</property> ++ <property name="fraction">0</property> ++ <property name="pulse_step">0.10000000149</property> ++ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> ++ </widget> ++ <packing> ++ <property name="padding">0</property> ++ <property name="expand">False</property> ++ <property name="fill">False</property> ++ </packing> ++ </child> ++ </widget> ++ </child> ++</widget> ++ ++<widget class="GtkDialog" id="pin_dialog"> ++ <property name="visible">False</property> ++ <property name="title" translatable="yes">PIN code required</property> ++ <property name="type">GTK_WINDOW_TOPLEVEL</property> ++ <property name="window_position">GTK_WIN_POS_NONE</property> ++ <property name="modal">False</property> ++ <property name="resizable">False</property> ++ <property name="destroy_with_parent">False</property> ++ <property name="decorated">True</property> ++ <property name="skip_taskbar_hint">False</property> ++ <property name="skip_pager_hint">False</property> ++ <property name="type_hint">GDK_WINDOW_TYPE_HINT_DIALOG</property> ++ <property name="gravity">GDK_GRAVITY_NORTH_WEST</property> ++ <property name="focus_on_map">True</property> ++ <property name="urgency_hint">False</property> ++ <property name="has_separator">True</property> ++ ++ <child internal-child="vbox"> ++ <widget class="GtkVBox" id="dialog-vbox3"> ++ <property name="homogeneous">False</property> ++ <property name="spacing">0</property> ++ ++ <child internal-child="action_area"> ++ <widget class="GtkHButtonBox" id="dialog-action_area3"> ++ <property name="visible">True</property> ++ <property name="layout_style">GTK_BUTTONBOX_END</property> ++ ++ <child> ++ <widget class="GtkButton" id="cancelbutton2"> ++ <property name="visible">True</property> ++ <property name="can_default">True</property> ++ <property name="can_focus">True</property> ++ <property name="label">gtk-cancel</property> ++ <property name="use_stock">True</property> ++ <property name="relief">GTK_RELIEF_NORMAL</property> ++ <property name="focus_on_click">True</property> ++ <property name="response_id">-6</property> ++ </widget> ++ </child> ++ ++ <child> ++ <widget class="GtkButton" id="okbutton2"> ++ <property name="visible">True</property> ++ <property name="can_default">True</property> ++ <property name="can_focus">True</property> ++ <property name="label">gtk-ok</property> ++ <property name="use_stock">True</property> ++ <property name="relief">GTK_RELIEF_NORMAL</property> ++ <property name="focus_on_click">True</property> ++ <property name="response_id">-5</property> ++ </widget> ++ </child> ++ </widget> ++ <packing> ++ <property name="padding">0</property> ++ <property name="expand">False</property> ++ <property name="fill">True</property> ++ <property name="pack_type">GTK_PACK_END</property> ++ </packing> ++ </child> ++ ++ <child> ++ <widget class="GtkVBox" id="vbox4"> ++ <property name="border_width">6</property> ++ <property name="visible">True</property> ++ <property name="homogeneous">False</property> ++ <property name="spacing">6</property> ++ ++ <child> ++ <widget class="GtkLabel" id="pin_dialog_label"> ++ <property name="visible">True</property> ++ <property name="label" translatable="yes">Please enter SIM PIN code</property> ++ <property name="use_underline">False</property> ++ <property name="use_markup">False</property> ++ <property name="justify">GTK_JUSTIFY_LEFT</property> ++ <property name="wrap">False</property> ++ <property name="selectable">False</property> ++ <property name="xalign">0.5</property> ++ <property name="yalign">0.5</property> ++ <property name="xpad">0</property> ++ <property name="ypad">0</property> ++ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> ++ <property name="width_chars">-1</property> ++ <property name="single_line_mode">False</property> ++ <property name="angle">0</property> ++ </widget> ++ <packing> ++ <property name="padding">0</property> ++ <property name="expand">False</property> ++ <property name="fill">False</property> ++ </packing> ++ </child> ++ ++ <child> ++ <widget class="GtkEntry" id="pin_dialog_entry"> ++ <property name="visible">True</property> ++ <property name="can_focus">True</property> ++ <property name="editable">True</property> ++ <property name="visibility">True</property> ++ <property name="max_length">0</property> ++ <property name="text" translatable="yes"></property> ++ <property name="has_frame">True</property> ++ <property name="invisible_char">●</property> ++ <property name="activates_default">False</property> ++ </widget> ++ <packing> ++ <property name="padding">0</property> ++ <property name="expand">False</property> ++ <property name="fill">False</property> ++ </packing> ++ </child> ++ </widget> ++ <packing> ++ <property name="padding">0</property> ++ <property name="expand">True</property> ++ <property name="fill">True</property> ++ </packing> ++ </child> ++ </widget> ++ </child> ++</widget> ++ ++</glade-interface> +diff --git a/src/nma-gsm-modem.c b/src/nma-gsm-modem.c +new file mode 100644 +index 0000000..0dba9cd +--- /dev/null ++++ b/src/nma-gsm-modem.c +@@ -0,0 +1,198 @@ ++/* -*- Mode: C; tab-width: 5; indent-tabs-mode: t; c-basic-offset: 5 -*- */ ++ ++#include "nma-gsm-modem.h" ++#include "mm-types.h" ++ ++G_DEFINE_TYPE (NMAGsmModem, nma_gsm_modem, G_TYPE_OBJECT) ++ ++#define NMA_GSM_MODEM_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NMA_TYPE_GSM_MODEM, NMAGsmModemPrivate)) ++ ++typedef struct { ++ DBusGProxy *proxy; ++ int signal_quality; ++ ++ gboolean disposed; ++} NMAGsmModemPrivate; ++ ++enum { ++ SIGNAL_QUALITY, ++ NETWORK_MODE, ++ ++ LAST_SIGNAL ++}; ++ ++static guint signals[LAST_SIGNAL] = { 0 }; ++ ++static void ++signal_quality_proxy (DBusGProxy *proxy, ++ guint32 signal_quality, ++ gpointer user_data) ++{ ++ NMAGsmModem *modem = NMA_GSM_MODEM (user_data); ++ ++ NMA_GSM_MODEM_GET_PRIVATE (modem)->signal_quality = signal_quality; ++ ++ g_signal_emit (modem, signals[SIGNAL_QUALITY], 0, signal_quality); ++} ++ ++static void ++network_mode_proxy (DBusGProxy *proxy, ++ guint32 network_mode, ++ gpointer user_data) ++{ ++ NMAGsmModem *modem = NMA_GSM_MODEM (user_data); ++ ++ g_signal_emit (modem, signals[NETWORK_MODE], 0, network_mode); ++} ++ ++NMAGsmModem * ++nma_gsm_modem_new (DBusGConnection *bus, const char *object_path) ++{ ++ NMAGsmModem *modem; ++ NMAGsmModemPrivate *priv; ++ ++ g_return_val_if_fail (bus != NULL, NULL); ++ g_return_val_if_fail (object_path != NULL, NULL); ++ ++ modem = (NMAGsmModem *) g_object_new (NMA_TYPE_GSM_MODEM, NULL); ++ if (!modem) ++ return NULL; ++ ++ priv = NMA_GSM_MODEM_GET_PRIVATE (modem); ++ priv->proxy = dbus_g_proxy_new_for_name (bus, MM_DBUS_SERVICE, object_path, MM_DBUS_INTERFACE_MODEM_GSM); ++ ++ dbus_g_proxy_add_signal (priv->proxy, "SignalQuality", G_TYPE_UINT, G_TYPE_INVALID); ++ dbus_g_proxy_connect_signal (priv->proxy, "SignalQuality", ++ G_CALLBACK (signal_quality_proxy), ++ modem, ++ NULL); ++ ++ dbus_g_proxy_add_signal (priv->proxy, "NetworkMode", G_TYPE_UINT, G_TYPE_INVALID); ++ dbus_g_proxy_connect_signal (priv->proxy, "NetworkMode", ++ G_CALLBACK (network_mode_proxy), ++ modem, ++ NULL); ++ ++ return modem; ++} ++ ++guint32 ++nma_gsm_modem_get_signal_quality (NMAGsmModem *modem) ++{ ++ NMAGsmModemPrivate *priv = NMA_GSM_MODEM_GET_PRIVATE (modem); ++ GError *err = NULL; ++ ++ g_return_val_if_fail (NMA_IS_GSM_MODEM (modem), 0); ++ ++ if (priv->signal_quality == -1) { ++ if (!dbus_g_proxy_call (priv->proxy, "GetSignalQuality", &err, ++ G_TYPE_INVALID, ++ G_TYPE_UINT, &priv->signal_quality, ++ G_TYPE_INVALID)) { ++ g_warning ("Error in getting signal quality: %s", err->message); ++ g_error_free (err); ++ } ++ } ++ ++ return priv->signal_quality; ++} ++ ++guint32 ++nma_gsm_modem_get_registration_info (NMAGsmModem *modem, ++ char **operator_code, ++ char **operator_name) ++{ ++ NMAGsmModemPrivate *priv = NMA_GSM_MODEM_GET_PRIVATE (modem); ++ GError *err = NULL; ++ guint32 status = MM_GSM_MODEM_REG_STATUS_UNKNOWN; ++ ++ g_return_val_if_fail (NMA_IS_GSM_MODEM (modem), 0); ++ ++ if (!dbus_g_proxy_call (priv->proxy, "GetRegistrationInfo", &err, ++ G_TYPE_INVALID, ++ G_TYPE_UINT, &status, ++ G_TYPE_STRING, operator_code, ++ G_TYPE_STRING, operator_name, ++ G_TYPE_INVALID)) { ++ g_warning ("Error in getting network mode: %s", err->message); ++ g_error_free (err); ++ } ++ ++ return status; ++} ++ ++guint32 ++nma_gsm_modem_get_network_mode (NMAGsmModem *modem) ++{ ++ NMAGsmModemPrivate *priv = NMA_GSM_MODEM_GET_PRIVATE (modem); ++ GError *err = NULL; ++ guint32 network_mode = 0; ++ ++ g_return_val_if_fail (NMA_IS_GSM_MODEM (modem), 0); ++ ++ if (!dbus_g_proxy_call (priv->proxy, "GetNetworkMode", &err, ++ G_TYPE_INVALID, ++ G_TYPE_UINT, &network_mode, ++ G_TYPE_INVALID)) { ++ g_warning ("Error in getting network mode: %s", err->message); ++ g_error_free (err); ++ } ++ ++ return network_mode; ++} ++ ++static void ++nma_gsm_modem_init (NMAGsmModem *modem) ++{ ++ NMAGsmModemPrivate *priv = NMA_GSM_MODEM_GET_PRIVATE (modem); ++ ++ priv->signal_quality = -1; ++} ++ ++static void ++dispose (GObject *object) ++{ ++ NMAGsmModemPrivate *priv = NMA_GSM_MODEM_GET_PRIVATE (object); ++ ++ if (priv->disposed) ++ return; ++ ++ priv->disposed = TRUE; ++ ++ if (priv->proxy) ++ g_object_unref (priv->proxy); ++ ++ G_OBJECT_CLASS (nma_gsm_modem_parent_class)->dispose (object); ++} ++ ++static void ++nma_gsm_modem_class_init (NMAGsmModemClass *modem_class) ++{ ++ GObjectClass *object_class = G_OBJECT_CLASS (modem_class); ++ ++ g_type_class_add_private (modem_class, sizeof (NMAGsmModemPrivate)); ++ ++ /* virtual methods */ ++ object_class->dispose = dispose; ++ ++ /* Signals */ ++ signals[SIGNAL_QUALITY] = ++ g_signal_new ("signal-quality", ++ G_OBJECT_CLASS_TYPE (object_class), ++ G_SIGNAL_RUN_FIRST, ++ G_STRUCT_OFFSET (NMAGsmModemClass, signal_quality), ++ NULL, NULL, ++ g_cclosure_marshal_VOID__UINT, ++ G_TYPE_NONE, 1, ++ G_TYPE_UINT); ++ ++ signals[NETWORK_MODE] = ++ g_signal_new ("network-mode", ++ G_OBJECT_CLASS_TYPE (object_class), ++ G_SIGNAL_RUN_FIRST, ++ G_STRUCT_OFFSET (NMAGsmModemClass, network_mode), ++ NULL, NULL, ++ g_cclosure_marshal_VOID__UINT, ++ G_TYPE_NONE, 1, ++ G_TYPE_UINT); ++} +diff --git a/src/nma-gsm-modem.h b/src/nma-gsm-modem.h +new file mode 100644 +index 0000000..90e7ae0 +--- /dev/null ++++ b/src/nma-gsm-modem.h +@@ -0,0 +1,45 @@ ++/* -*- Mode: C; tab-width: 5; indent-tabs-mode: t; c-basic-offset: 5 -*- */ ++ ++#ifndef NMA_GSM_MODEM_H ++#define NMA_GSM_MODEM_H ++ ++#include <glib/gtypes.h> ++#include <glib-object.h> ++#include <dbus/dbus-glib.h> ++ ++G_BEGIN_DECLS ++ ++#define NMA_TYPE_GSM_MODEM (nma_gsm_modem_get_type ()) ++#define NMA_GSM_MODEM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMA_TYPE_GSM_MODEM, NMAGsmModem)) ++#define NMA_GSM_MODEM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NMA_TYPE_GSM_MODEM, NMAGsmModemClass)) ++#define NMA_IS_GSM_MODEM(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NMA_TYPE_GSM_MODEM)) ++#define NMA_IS_GSM_MODEM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), NMA_TYPE_GSM_MODEM)) ++#define NMA_GSM_MODEM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NMA_TYPE_GSM_MODEM, NMAGsmModemClass)) ++ ++typedef struct { ++ GObject parent; ++} NMAGsmModem; ++ ++typedef struct { ++ GObjectClass parent; ++ ++ /* Signals */ ++ void (*signal_quality) (NMAGsmModem *modem, guint32 signal_quality); ++ void (*network_mode) (NMAGsmModem *modem, guint32 network_mode); ++} NMAGsmModemClass; ++ ++GType nma_gsm_modem_get_type (void); ++ ++NMAGsmModem *nma_gsm_modem_new (DBusGConnection *bus, ++ const char *object_path); ++ ++guint32 nma_gsm_modem_get_signal_quality (NMAGsmModem *modem); ++guint32 nma_gsm_modem_get_registration_info (NMAGsmModem *modem, ++ char **operator_code, ++ char **operator_name); ++ ++guint32 nma_gsm_modem_get_network_mode (NMAGsmModem *modem); ++ ++G_END_DECLS ++ ++#endif /* NMA_GSM_MODEM_H */ diff --git a/org.freedesktop.ModemManager.conf b/org.freedesktop.ModemManager.conf new file mode 100644 index 0000000..5d62d7a --- /dev/null +++ b/org.freedesktop.ModemManager.conf @@ -0,0 +1,14 @@ +<!DOCTYPE busconfig PUBLIC + "-//freedesktop//DTD D-BUS Bus Configuration 1.0//EN" + "http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd"> +<busconfig> + <policy context="default"> + <allow send_destination="org.freedesktop.ModemManager"/> + </policy> + + <policy user="root"> + <allow own="org.freedesktop.ModemManager"/> + </policy> + + <limit name="max_replies_per_connection">512</limit> +</busconfig> diff --git a/org.freedesktop.ModemManager.service.in b/org.freedesktop.ModemManager.service.in new file mode 100644 index 0000000..2b73cce --- /dev/null +++ b/org.freedesktop.ModemManager.service.in @@ -0,0 +1,4 @@ +[D-BUS Service] +Name=org.freedesktop.ModemManager +Exec=@sbindir@/modem-manager +User=root diff --git a/plugins/77-mm-ericsson-mbm.rules b/plugins/77-mm-ericsson-mbm.rules new file mode 100644 index 0000000..71dc6b8 --- /dev/null +++ b/plugins/77-mm-ericsson-mbm.rules @@ -0,0 +1,41 @@ +# do not edit this file, it will be overwritten on update + +ACTION!="add|change", GOTO="mm_mbm_end" +SUBSYSTEM!="usb", GOTO="mm_mbm_end" +ENV{DEVTYPE}!="usb_device", GOTO="mm_mbm_end" + +# Ericsson F3507g +ATTRS{idVendor}=="0bdb", ATTRS{idProduct}=="1900", ENV{ID_MM_ERICSSON_MBM}="1" +ATTRS{idVendor}=="0bdb", ATTRS{idProduct}=="1902", ENV{ID_MM_ERICSSON_MBM}="1" + +# Ericsson F3607gw +ATTRS{idVendor}=="0bdb", ATTRS{idProduct}=="1904", ENV{ID_MM_ERICSSON_MBM}="1" +ATTRS{idVendor}=="0bdb", ATTRS{idProduct}=="1905", ENV{ID_MM_ERICSSON_MBM}="1" +ATTRS{idVendor}=="0bdb", ATTRS{idProduct}=="1906", ENV{ID_MM_ERICSSON_MBM}="1" + +# Ericsson F3307 +ATTRS{idVendor}=="0bdb", ATTRS{idProduct}=="190a", ENV{ID_MM_ERICSSON_MBM}="1" +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" + +# Sony-Ericsson MD300 +ATTRS{idVendor}=="0fce", ATTRS{idProduct}=="d0cf", ENV{ID_MM_ERICSSON_MBM}="1" + +# Dell 5530 HSDPA +ATTRS{idVendor}=="413c", ATTRS{idProduct}=="8147", ENV{ID_MM_ERICSSON_MBM}="1" + +# Dell F3607gw +ATTRS{idVendor}=="413c", ATTRS{idProduct}=="8183", ENV{ID_MM_ERICSSON_MBM}="1" +ATTRS{idVendor}=="413c", ATTRS{idProduct}=="8184", ENV{ID_MM_ERICSSON_MBM}="1" + +# Toshiba +ATTRS{idVendor}=="0930", ATTRS{idProduct}=="130b", ENV{ID_MM_ERICSSON_MBM}="1" + +# Toshiba F3607gw +ATTRS{idVendor}=="0930", ATTRS{idProduct}=="130c", ENV{ID_MM_ERICSSON_MBM}="1" +ATTRS{idVendor}=="0930", ATTRS{idProduct}=="1311", ENV{ID_MM_ERICSSON_MBM}="1" + +LABEL="mm_mbm_end" + diff --git a/plugins/77-mm-longcheer-port-types.rules b/plugins/77-mm-longcheer-port-types.rules new file mode 100644 index 0000000..7317df7 --- /dev/null +++ b/plugins/77-mm-longcheer-port-types.rules @@ -0,0 +1,45 @@ +# do not edit this file, it will be overwritten on update + +# Longcheer makes modules that other companies rebrand, like: +# +# Alcatel One Touch X020 +# Alcatel One Touch X030 +# MobiData MBD-200HU +# ST Mobile Connect HSUPA USB Modem + + +ACTION!="add|change", GOTO="mm_longcheer_port_types_end" +SUBSYSTEM!="tty", GOTO="mm_longcheer_port_types_end" + +SUBSYSTEMS=="usb", ATTRS{idVendor}=="1c9e", GOTO="mm_longcheer_vendorcheck" +SUBSYSTEMS=="usb", ATTRS{idVendor}=="1bbb", GOTO="mm_tamobile_vendorcheck" +GOTO="mm_longcheer_port_types_end" + +LABEL="mm_longcheer_vendorcheck" +SUBSYSTEMS=="usb", ATTRS{bInterfaceNumber}=="?*", ENV{.MM_USBIFNUM}="$attr{bInterfaceNumber}" + +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" + +# Alcatel One Touch X020 +ATTRS{idProduct}=="6061", ENV{.MM_USBIFNUM}=="00", ENV{ID_MM_LONGCHEER_PORT_TYPE_MODEM}="1" +ATTRS{idProduct}=="6061", ENV{.MM_USBIFNUM}=="02", ENV{ID_MM_LONGCHEER_PORT_TYPE_AUX}="1" +ATTRS{idProduct}=="6061", ENV{ID_MM_LONGCHEER_TAGGED}="1" + +GOTO="mm_longcheer_port_types_end" + + +LABEL="mm_tamobile_vendorcheck" +SUBSYSTEMS=="usb", ATTRS{bInterfaceNumber}=="?*", ENV{.MM_USBIFNUM}="$attr{bInterfaceNumber}" + +# Alcatel One Touch X060s +ATTRS{idProduct}=="0000", ENV{.MM_USBIFNUM}=="03", ENV{ID_MM_LONGCHEER_PORT_TYPE_MODEM}="1" +ATTRS{idProduct}=="0000", ENV{.MM_USBIFNUM}=="01", ENV{ID_MM_LONGCHEER_PORT_TYPE_AUX}="1" +ATTRS{idProduct}=="0000", ENV{ID_MM_LONGCHEER_TAGGED}="1" + +GOTO="mm_longcheer_port_types_end" + + +LABEL="mm_longcheer_port_types_end" + diff --git a/plugins/77-mm-zte-port-types.rules b/plugins/77-mm-zte-port-types.rules new file mode 100644 index 0000000..9d64aa9 --- /dev/null +++ b/plugins/77-mm-zte-port-types.rules @@ -0,0 +1,115 @@ +# do not edit this file, it will be overwritten on update + +ACTION!="add|change", GOTO="mm_zte_port_types_end" +SUBSYSTEM!="tty", GOTO="mm_zte_port_types_end" + +SUBSYSTEMS=="usb", ATTRS{idVendor}=="19d2", GOTO="mm_zte_port_types_vendorcheck" +GOTO="mm_zte_port_types_end" + +LABEL="mm_zte_port_types_vendorcheck" +SUBSYSTEMS=="usb", ATTRS{bInterfaceNumber}=="?*", ENV{.MM_USBIFNUM}="$attr{bInterfaceNumber}" + +ATTRS{idProduct}=="0001", ENV{.MM_USBIFNUM}=="00", ENV{ID_MM_ZTE_PORT_TYPE_MODEM}="1" +ATTRS{idProduct}=="0001", ENV{.MM_USBIFNUM}=="02", ENV{ID_MM_ZTE_PORT_TYPE_AUX}="1" + +ATTRS{idProduct}=="0002", ENV{.MM_USBIFNUM}=="02", ENV{ID_MM_ZTE_PORT_TYPE_MODEM}="1" +ATTRS{idProduct}=="0002", ENV{.MM_USBIFNUM}=="04", ENV{ID_MM_ZTE_PORT_TYPE_AUX}="1" + +ATTRS{idProduct}=="0003", ENV{.MM_USBIFNUM}=="00", ENV{ID_MM_ZTE_PORT_TYPE_MODEM}="1" +ATTRS{idProduct}=="0003", ENV{.MM_USBIFNUM}=="01", ENV{ID_MM_ZTE_PORT_TYPE_AUX}="1" + +ATTRS{idProduct}=="0004", ENV{.MM_USBIFNUM}=="00", ENV{ID_MM_ZTE_PORT_TYPE_MODEM}="1" +ATTRS{idProduct}=="0004", ENV{.MM_USBIFNUM}=="01", ENV{ID_MM_ZTE_PORT_TYPE_AUX}="1" + +ATTRS{idProduct}=="0005", ENV{.MM_USBIFNUM}=="00", ENV{ID_MM_ZTE_PORT_TYPE_MODEM}="1" +ATTRS{idProduct}=="0005", ENV{.MM_USBIFNUM}=="01", ENV{ID_MM_ZTE_PORT_TYPE_AUX}="1" + +ATTRS{idProduct}=="0006", ENV{.MM_USBIFNUM}=="00", ENV{ID_MM_ZTE_PORT_TYPE_MODEM}="1" +ATTRS{idProduct}=="0006", ENV{.MM_USBIFNUM}=="01", ENV{ID_MM_ZTE_PORT_TYPE_AUX}="1" + +ATTRS{idProduct}=="0007", ENV{.MM_USBIFNUM}=="00", ENV{ID_MM_ZTE_PORT_TYPE_MODEM}="1" +ATTRS{idProduct}=="0007", ENV{.MM_USBIFNUM}=="01", ENV{ID_MM_ZTE_PORT_TYPE_AUX}="1" + +ATTRS{idProduct}=="0008", ENV{.MM_USBIFNUM}=="00", ENV{ID_MM_ZTE_PORT_TYPE_MODEM}="1" +ATTRS{idProduct}=="0008", ENV{.MM_USBIFNUM}=="01", ENV{ID_MM_ZTE_PORT_TYPE_AUX}="1" + +ATTRS{idProduct}=="0009", ENV{.MM_USBIFNUM}=="00", ENV{ID_MM_ZTE_PORT_TYPE_MODEM}="1" +ATTRS{idProduct}=="0009", ENV{.MM_USBIFNUM}=="01", ENV{ID_MM_ZTE_PORT_TYPE_AUX}="1" + +ATTRS{idProduct}=="000A", ENV{.MM_USBIFNUM}=="00", ENV{ID_MM_ZTE_PORT_TYPE_MODEM}="1" +ATTRS{idProduct}=="000A", ENV{.MM_USBIFNUM}=="01", ENV{ID_MM_ZTE_PORT_TYPE_AUX}="1" + +ATTRS{idProduct}=="0012", ENV{.MM_USBIFNUM}=="02", ENV{ID_MM_ZTE_PORT_TYPE_MODEM}="1" +ATTRS{idProduct}=="0012", ENV{.MM_USBIFNUM}=="04", ENV{ID_MM_ZTE_PORT_TYPE_AUX}="1" + +ATTRS{idProduct}=="0015", ENV{.MM_USBIFNUM}=="01", ENV{ID_MM_ZTE_PORT_TYPE_MODEM}="1" +ATTRS{idProduct}=="0015", ENV{.MM_USBIFNUM}=="03", ENV{ID_MM_ZTE_PORT_TYPE_AUX}="1" + +ATTRS{idProduct}=="0016", ENV{.MM_USBIFNUM}=="02", ENV{ID_MM_ZTE_PORT_TYPE_MODEM}="1" +ATTRS{idProduct}=="0016", ENV{.MM_USBIFNUM}=="01", ENV{ID_MM_ZTE_PORT_TYPE_AUX}="1" + +ATTRS{idProduct}=="0017", ENV{.MM_USBIFNUM}=="02", ENV{ID_MM_ZTE_PORT_TYPE_MODEM}="1" +ATTRS{idProduct}=="0017", ENV{.MM_USBIFNUM}=="01", ENV{ID_MM_ZTE_PORT_TYPE_AUX}="1" + +ATTRS{idProduct}=="0018", ENV{.MM_USBIFNUM}=="02", ENV{ID_MM_ZTE_PORT_TYPE_MODEM}="1" +ATTRS{idProduct}=="0018", ENV{.MM_USBIFNUM}=="01", ENV{ID_MM_ZTE_PORT_TYPE_AUX}="1" + +ATTRS{idProduct}=="0019", ENV{.MM_USBIFNUM}=="02", ENV{ID_MM_ZTE_PORT_TYPE_MODEM}="1" +ATTRS{idProduct}=="0019", ENV{.MM_USBIFNUM}=="01", ENV{ID_MM_ZTE_PORT_TYPE_AUX}="1" + +ATTRS{idProduct}=="0021", ENV{.MM_USBIFNUM}=="03", ENV{ID_MM_ZTE_PORT_TYPE_MODEM}="1" +ATTRS{idProduct}=="0021", ENV{.MM_USBIFNUM}=="01", ENV{ID_MM_ZTE_PORT_TYPE_AUX}="1" + +ATTRS{idProduct}=="0024", ENV{.MM_USBIFNUM}=="02", ENV{ID_MM_ZTE_PORT_TYPE_MODEM}="1" +ATTRS{idProduct}=="0024", ENV{.MM_USBIFNUM}=="04", ENV{ID_MM_ZTE_PORT_TYPE_AUX}="1" + +ATTRS{idProduct}=="0025", ENV{.MM_USBIFNUM}=="02", ENV{ID_MM_ZTE_PORT_TYPE_MODEM}="1" +ATTRS{idProduct}=="0025", ENV{.MM_USBIFNUM}=="04", ENV{ID_MM_ZTE_PORT_TYPE_AUX}="1" + +ATTRS{idProduct}=="0030", ENV{.MM_USBIFNUM}=="01", ENV{ID_MM_ZTE_PORT_TYPE_MODEM}="1" +ATTRS{idProduct}=="0030", ENV{.MM_USBIFNUM}=="03", ENV{ID_MM_ZTE_PORT_TYPE_AUX}="1" + +ATTRS{idProduct}=="0031", ENV{.MM_USBIFNUM}=="03", ENV{ID_MM_ZTE_PORT_TYPE_MODEM}="1" +ATTRS{idProduct}=="0031", ENV{.MM_USBIFNUM}=="01", ENV{ID_MM_ZTE_PORT_TYPE_AUX}="1" + +ATTRS{idProduct}=="0033", ENV{.MM_USBIFNUM}=="04", ENV{ID_MM_ZTE_PORT_TYPE_MODEM}="1" +ATTRS{idProduct}=="0033", ENV{.MM_USBIFNUM}=="01", ENV{ID_MM_ZTE_PORT_TYPE_AUX}="1" + +ATTRS{idProduct}=="0037", ENV{.MM_USBIFNUM}=="03", ENV{ID_MM_ZTE_PORT_TYPE_MODEM}="1" +ATTRS{idProduct}=="0037", ENV{.MM_USBIFNUM}=="01", ENV{ID_MM_ZTE_PORT_TYPE_AUX}="1" + +ATTRS{idProduct}=="0042", ENV{.MM_USBIFNUM}=="03", ENV{ID_MM_ZTE_PORT_TYPE_MODEM}="1" +ATTRS{idProduct}=="0042", ENV{.MM_USBIFNUM}=="01", ENV{ID_MM_ZTE_PORT_TYPE_AUX}="1" + +ATTRS{idProduct}=="0043", ENV{.MM_USBIFNUM}=="03", ENV{ID_MM_ZTE_PORT_TYPE_MODEM}="1" +ATTRS{idProduct}=="0043", ENV{.MM_USBIFNUM}=="02", ENV{ID_MM_ZTE_PORT_TYPE_AUX}="1" + +ATTRS{idProduct}=="0048", ENV{.MM_USBIFNUM}=="04", ENV{ID_MM_ZTE_PORT_TYPE_MODEM}="1" +ATTRS{idProduct}=="0048", ENV{.MM_USBIFNUM}=="02", ENV{ID_MM_ZTE_PORT_TYPE_AUX}="1" + +ATTRS{idProduct}=="0049", ENV{.MM_USBIFNUM}=="04", ENV{ID_MM_ZTE_PORT_TYPE_MODEM}="1" +ATTRS{idProduct}=="0049", ENV{.MM_USBIFNUM}=="02", ENV{ID_MM_ZTE_PORT_TYPE_AUX}="1" + +ATTRS{idProduct}=="0052", ENV{.MM_USBIFNUM}=="03", ENV{ID_MM_ZTE_PORT_TYPE_MODEM}="1" +ATTRS{idProduct}=="0052", ENV{.MM_USBIFNUM}=="01", ENV{ID_MM_ZTE_PORT_TYPE_AUX}="1" + +ATTRS{idProduct}=="0055", ENV{.MM_USBIFNUM}=="02", ENV{ID_MM_ZTE_PORT_TYPE_MODEM}="1" +ATTRS{idProduct}=="0055", ENV{.MM_USBIFNUM}=="04", ENV{ID_MM_ZTE_PORT_TYPE_AUX}="1" + +ATTRS{idProduct}=="0061", ENV{.MM_USBIFNUM}=="03", ENV{ID_MM_ZTE_PORT_TYPE_MODEM}="1" +ATTRS{idProduct}=="0061", ENV{.MM_USBIFNUM}=="01", ENV{ID_MM_ZTE_PORT_TYPE_AUX}="1" + +ATTRS{idProduct}=="0063", ENV{.MM_USBIFNUM}=="03", ENV{ID_MM_ZTE_PORT_TYPE_MODEM}="1" +ATTRS{idProduct}=="0063", ENV{.MM_USBIFNUM}=="01", ENV{ID_MM_ZTE_PORT_TYPE_AUX}="1" + +ATTRS{idProduct}=="0064", ENV{.MM_USBIFNUM}=="02", ENV{ID_MM_ZTE_PORT_TYPE_MODEM}="1" +ATTRS{idProduct}=="0064", ENV{.MM_USBIFNUM}=="00", ENV{ID_MM_ZTE_PORT_TYPE_AUX}="1" + +ATTRS{idProduct}=="0066", ENV{.MM_USBIFNUM}=="03", ENV{ID_MM_ZTE_PORT_TYPE_MODEM}="1" +ATTRS{idProduct}=="0066", ENV{.MM_USBIFNUM}=="01", ENV{ID_MM_ZTE_PORT_TYPE_AUX}="1" + +ATTRS{idProduct}=="2002", ENV{.MM_USBIFNUM}=="03", ENV{ID_MM_ZTE_PORT_TYPE_MODEM}="1" +ATTRS{idProduct}=="2002", ENV{.MM_USBIFNUM}=="01", ENV{ID_MM_ZTE_PORT_TYPE_AUX}="1" + +LABEL="mm_zte_port_types_end" + diff --git a/plugins/Makefile.am b/plugins/Makefile.am new file mode 100644 index 0000000..a361358 --- /dev/null +++ b/plugins/Makefile.am @@ -0,0 +1,268 @@ +pkglib_LTLIBRARIES = \ + libmm-plugin-generic.la \ + libmm-plugin-moto-c.la \ + libmm-plugin-gobi.la \ + libmm-plugin-huawei.la \ + libmm-plugin-hso.la \ + libmm-plugin-option.la \ + libmm-plugin-sierra.la \ + libmm-plugin-novatel.la \ + libmm-plugin-nokia.la \ + libmm-plugin-zte.la \ + libmm-plugin-mbm.la \ + libmm-plugin-longcheer.la \ + libmm-plugin-anydata.la + +# Generic + +libmm_plugin_generic_la_SOURCES = \ + mm-plugin-generic.c \ + mm-plugin-generic.h + +libmm_plugin_generic_la_CPPFLAGS = \ + $(MM_CFLAGS) \ + $(GUDEV_CFLAGS) \ + -I$(top_srcdir)/src + +libmm_plugin_generic_la_LDFLAGS = \ + $(GUDEV_LDFLAGS) \ + -module \ + -avoid-version + +# Motorola C-series phones + +libmm_plugin_moto_c_la_SOURCES = \ + mm-plugin-moto-c.c \ + mm-plugin-moto-c.h \ + mm-modem-moto-c-gsm.c \ + mm-modem-moto-c-gsm.h + +libmm_plugin_moto_c_la_CPPFLAGS = \ + $(MM_CFLAGS) \ + $(GUDEV_CFLAGS) \ + -I$(top_srcdir)/src + +libmm_plugin_moto_c_la_LDFLAGS = \ + $(GUDEV_LDFLAGS) \ + -module \ + -avoid-version + +# Qualcomm Gobi + +libmm_plugin_gobi_la_SOURCES = \ + mm-plugin-gobi.c \ + mm-plugin-gobi.h \ + mm-modem-gobi-gsm.c \ + mm-modem-gobi-gsm.h + +libmm_plugin_gobi_la_CPPFLAGS = \ + $(MM_CFLAGS) \ + $(GUDEV_CFLAGS) \ + -I$(top_srcdir)/src + +libmm_plugin_gobi_la_LDFLAGS = \ + $(GUDEV_LDFLAGS) \ + -module \ + -avoid-version + +# Huawei + +libmm_plugin_huawei_la_SOURCES = \ + mm-plugin-huawei.c \ + mm-plugin-huawei.h \ + mm-modem-huawei-gsm.c \ + mm-modem-huawei-gsm.h \ + mm-modem-huawei-cdma.c \ + mm-modem-huawei-cdma.h + +libmm_plugin_huawei_la_CPPFLAGS = \ + $(MM_CFLAGS) \ + $(GUDEV_CFLAGS) \ + -I$(top_srcdir)/src + +libmm_plugin_huawei_la_LDFLAGS = \ + $(GUDEV_LDFLAGS) \ + -module \ + -avoid-version + +# HSO + +libmm_plugin_hso_la_SOURCES = \ + mm-plugin-hso.c \ + mm-plugin-hso.h \ + mm-modem-gsm-hso-glue.h \ + mm-modem-hso.c \ + mm-modem-hso.h + +mm-modem-gsm-hso-glue.h: $(top_srcdir)/introspection/mm-modem-gsm-hso.xml + dbus-binding-tool --prefix=mm_modem_gsm_hso --mode=glib-server --output=$@ $< + +libmm_plugin_hso_la_CPPFLAGS = \ + $(MM_CFLAGS) \ + $(GUDEV_CFLAGS) \ + -I$(top_srcdir)/src + +libmm_plugin_hso_la_LDFLAGS = \ + $(GUDEV_LDFLAGS) \ + -module \ + -avoid-version + +# MBM + +libmm_plugin_mbm_la_SOURCES = \ + mm-plugin-mbm.c \ + mm-plugin-mbm.h \ + mm-modem-mbm.c \ + mm-modem-mbm.h + +libmm_plugin_mbm_la_CPPFLAGS = \ + $(MM_CFLAGS) \ + $(GUDEV_CFLAGS) \ + -I$(top_srcdir)/src + +libmm_plugin_mbm_la_LDFLAGS = \ + $(GUDEV_LDFLAGS) \ + -module \ + -avoid-version + +# Option + +libmm_plugin_option_la_SOURCES = \ + mm-plugin-option.c \ + mm-plugin-option.h \ + mm-modem-option.c \ + mm-modem-option.h + +libmm_plugin_option_la_CPPFLAGS = \ + $(MM_CFLAGS) \ + $(GUDEV_CFLAGS) \ + -I$(top_srcdir)/src + +libmm_plugin_option_la_LDFLAGS = \ + $(GUDEV_LDFLAGS) \ + -module \ + -avoid-version + +# Sierra + +libmm_plugin_sierra_la_SOURCES = \ + mm-plugin-sierra.c \ + mm-plugin-sierra.h \ + mm-modem-sierra-gsm.c \ + mm-modem-sierra-gsm.h \ + mm-modem-sierra-cdma.c \ + mm-modem-sierra-cdma.h + +libmm_plugin_sierra_la_CPPFLAGS = \ + $(MM_CFLAGS) \ + $(GUDEV_CFLAGS) \ + -I$(top_srcdir)/src + +libmm_plugin_sierra_la_LDFLAGS = \ + $(GUDEV_LDFLAGS) \ + -module \ + -avoid-version + +# Novatel + +libmm_plugin_novatel_la_SOURCES = \ + mm-plugin-novatel.c \ + mm-plugin-novatel.h \ + mm-modem-novatel-gsm.c \ + mm-modem-novatel-gsm.h + +libmm_plugin_novatel_la_CPPFLAGS = \ + $(MM_CFLAGS) \ + $(GUDEV_CFLAGS) \ + -I$(top_srcdir)/src + +libmm_plugin_novatel_la_LDFLAGS = \ + $(GUDEV_LDFLAGS) \ + -module \ + -avoid-version + +# Nokia + +libmm_plugin_nokia_la_SOURCES = \ + mm-plugin-nokia.c \ + mm-plugin-nokia.h \ + mm-modem-nokia.c \ + mm-modem-nokia.h + +libmm_plugin_nokia_la_CPPFLAGS = \ + $(MM_CFLAGS) \ + $(GUDEV_CFLAGS) \ + -I$(top_srcdir)/src + +libmm_plugin_nokia_la_LDFLAGS = \ + $(GUDEV_LDFLAGS) \ + -module \ + -avoid-version + +# Zte + +libmm_plugin_zte_la_SOURCES = \ + mm-plugin-zte.c \ + mm-plugin-zte.h \ + mm-modem-zte.c \ + mm-modem-zte.h + +libmm_plugin_zte_la_CPPFLAGS = \ + $(MM_CFLAGS) \ + $(GUDEV_CFLAGS) \ + -I$(top_srcdir)/src + +libmm_plugin_zte_la_LDFLAGS = \ + $(GUDEV_LDFLAGS) \ + -module \ + -avoid-version + +# Longcheer (and rebranded dongles) + +libmm_plugin_longcheer_la_SOURCES = \ + mm-plugin-longcheer.c \ + mm-plugin-longcheer.h + +libmm_plugin_longcheer_la_CPPFLAGS = \ + $(MM_CFLAGS) \ + $(GUDEV_CFLAGS) \ + -I$(top_srcdir)/src + +libmm_plugin_longcheer_la_LDFLAGS = \ + $(GUDEV_LDFLAGS) \ + -module \ + -avoid-version + +# AnyData CDMA + +libmm_plugin_anydata_la_SOURCES = \ + mm-plugin-anydata.c \ + mm-plugin-anydata.h \ + mm-modem-anydata-cdma.c \ + mm-modem-anydata-cdma.h + +libmm_plugin_anydata_la_CPPFLAGS = \ + $(MM_CFLAGS) \ + $(GUDEV_CFLAGS) \ + -I$(top_srcdir)/src + +libmm_plugin_anydata_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 + +BUILT_SOURCES = \ + mm-modem-gsm-hso-glue.h + +CLEANFILES = $(BUILT_SOURCES) + +EXTRA_DIST = \ + $(udevrules_DATA) + diff --git a/plugins/mm-modem-anydata-cdma.c b/plugins/mm-modem-anydata-cdma.c new file mode 100644 index 0000000..f6528ec --- /dev/null +++ b/plugins/mm-modem-anydata-cdma.c @@ -0,0 +1,384 @@ +/* -*- 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> + +#define G_UDEV_API_IS_SUBJECT_TO_CHANGE +#include <gudev/gudev.h> + +#include "mm-modem-anydata-cdma.h" +#include "mm-errors.h" +#include "mm-callback-info.h" +#include "mm-serial-port.h" +#include "mm-serial-parsers.h" + +static void modem_init (MMModem *modem_class); + +G_DEFINE_TYPE_EXTENDED (MMModemAnydataCdma, mm_modem_anydata_cdma, MM_TYPE_GENERIC_CDMA, 0, + G_IMPLEMENT_INTERFACE (MM_TYPE_MODEM, modem_init)) + +MMModem * +mm_modem_anydata_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_ANYDATA_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, FALSE, + NULL)); +} + +/*****************************************************************************/ + +static const char * +strip_response (const char *resp, const char *cmd) +{ + const char *p = resp; + + if (p) { + if (!strncmp (p, cmd, strlen (cmd))) + p += strlen (cmd); + while (*p == ' ') + p++; + } + return p; +} + +static gboolean +uint_from_match_item (GMatchInfo *match_info, guint32 num, guint32 *val) +{ + long int tmp; + char *str; + gboolean success = FALSE; + + str = g_match_info_fetch (match_info, num); + g_return_val_if_fail (str != NULL, FALSE); + + errno = 0; + tmp = strtol (str, NULL, 10); + if (errno == 0 && tmp >= 0 && tmp <= G_MAXUINT) { + *val = (guint32) tmp; + success = TRUE; + } + g_free (str); + return success; +} + +static gboolean +int_from_match_item (GMatchInfo *match_info, guint32 num, gint *val) +{ + long int tmp; + char *str; + gboolean success = FALSE; + + str = g_match_info_fetch (match_info, num); + g_return_val_if_fail (str != NULL, FALSE); + + errno = 0; + tmp = strtol (str, NULL, 10); + if (errno == 0 && tmp >= G_MININT && tmp <= G_MAXINT) { + *val = (gint) tmp; + success = TRUE; + } + g_free (str); + return success; +} + +static void +evdo_state_done (MMSerialPort *port, + GString *response, + GError *error, + gpointer user_data) +{ + MMCallbackInfo *info = (MMCallbackInfo *) user_data; + MMModemCdmaRegistrationState reg_state = MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN; + const char *reply; + 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); + } + + mm_callback_info_schedule (info); + return; + } + + reply = strip_response (response->str, "*HSTATE:"); + + /* Format is "<at state>,<session state>,<channel>,<pn>,<EcIo>,<rssi>,..." */ + r = g_regex_new ("\\s*(\\d+)\\s*,\\s*(\\d+)\\s*,\\s*(\\d+)\\s*,\\s*(\\d+)\\s*,\\s*([^,\\)]*)\\s*,\\s*([^,\\)]*)\\s*,.*", + 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_regex_match (r, reply, 0, &match_info); + if (g_match_info_get_match_count (match_info) >= 6) { + guint32 val = 0; + gint dbm = 0; + + /* dBm is between -106 (worst) and -20.7 (best) */ + int_from_match_item (match_info, 6, &dbm); + + /* Parse the EVDO radio state */ + if (uint_from_match_item (match_info, 1, &val)) { + switch (val) { + case 3: /* IDLE */ + /* If IDLE and the EVDO dBm is -105 or lower, assume no service. + * It may be that IDLE actually means NO SERVICE too; not sure. + */ + if (dbm > -105) + reg_state = MM_MODEM_CDMA_REGISTRATION_STATE_REGISTERED; + break; + case 4: /* ACCESS */ + case 5: /* CONNECT */ + reg_state = MM_MODEM_CDMA_REGISTRATION_STATE_REGISTERED; + break; + default: + g_message ("ANYDATA: unknown *STATE (%d); assuming no service.", val); + /* fall through */ + case 0: /* NO SERVICE */ + case 1: /* ACQUISITION */ + case 2: /* SYNC */ + break; + } + } + } + + mm_generic_cdma_query_reg_state_set_callback_evdo_state (info, reg_state); + + mm_callback_info_schedule (info); +} + +static void +state_done (MMSerialPort *port, + GString *response, + GError *error, + gpointer user_data) +{ + MMCallbackInfo *info = (MMCallbackInfo *) user_data; + MMModemCdmaRegistrationState reg_state = MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN; + const char *reply; + 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); + } + + mm_callback_info_schedule (info); + return; + } + + reply = strip_response (response->str, "*STATE:"); + + /* Format is "<channel>,<pn>,<sid>,<nid>,<state>,<rssi>,..." */ + 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)."); + mm_callback_info_schedule (info); + return; + } + + g_regex_match (r, reply, 0, &match_info); + if (g_match_info_get_match_count (match_info) >= 6) { + guint32 val = 0; + gint dbm = 0; + + /* dBm is between -106 (worst) and -20.7 (best) */ + int_from_match_item (match_info, 6, &dbm); + + /* Parse the 1x radio state */ + if (uint_from_match_item (match_info, 5, &val)) { + switch (val) { + case 1: /* IDLE */ + /* If IDLE and the 1X dBm is -105 or lower, assume no service. + * It may be that IDLE actually means NO SERVICE too; not sure. + */ + if (dbm > -105) + reg_state = MM_MODEM_CDMA_REGISTRATION_STATE_REGISTERED; + break; + case 2: /* ACCESS */ + case 3: /* PAGING */ + case 4: /* TRAFFIC */ + reg_state = MM_MODEM_CDMA_REGISTRATION_STATE_REGISTERED; + break; + default: + g_message ("ANYDATA: unknown *STATE (%d); assuming no service.", val); + /* fall through */ + case 0: /* NO SERVICE */ + break; + } + } + } + + 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); +} + +static void +query_registration_state (MMGenericCdma *cdma, + 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); + + 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; + } + + mm_serial_port_queue_command (port, "*STATE?", 3, state_done, info); +} + +/*****************************************************************************/ + +static gboolean +grab_port (MMModem *modem, + const char *subsys, + const char *name, + MMPortType suggested_type, + gpointer user_data, + GError **error) +{ + MMPort *port = NULL; + 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)) { + /* 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); + 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); + 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); + g_regex_unref (regex); + + /* Abnomral state notifications + * + * FIXME: set 1X/EVDO registration state to UNKNOWN when these + * notifications are received? + */ + + /* 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); + 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); + 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); + g_regex_unref (regex); + } + + return !!port; +} + +/*****************************************************************************/ + +static void +modem_init (MMModem *modem_class) +{ + modem_class->grab_port = grab_port; +} + +static void +mm_modem_anydata_cdma_init (MMModemAnydataCdma *self) +{ +} + +static void +mm_modem_anydata_cdma_class_init (MMModemAnydataCdmaClass *klass) +{ + MMGenericCdmaClass *cdma_class = MM_GENERIC_CDMA_CLASS (klass); + + mm_modem_anydata_cdma_parent_class = g_type_class_peek_parent (klass); + + cdma_class->query_registration_state = query_registration_state; + +#if 0 + /* FIXME: maybe use AT*SLEEP=0/1 to disable/enable slotted mode for powersave */ + cdma_class->post_enable = post_enable; + cdma_class->post_enable = post_disable; +#endif +} + diff --git a/plugins/mm-modem-anydata-cdma.h b/plugins/mm-modem-anydata-cdma.h new file mode 100644 index 0000000..d2695d5 --- /dev/null +++ b/plugins/mm-modem-anydata-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_ANYDATA_CDMA_H +#define MM_MODEM_ANYDATA_CDMA_H + +#include "mm-generic-cdma.h" + +#define MM_TYPE_MODEM_ANYDATA_CDMA (mm_modem_anydata_cdma_get_type ()) +#define MM_MODEM_ANYDATA_CDMA(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_MODEM_ANYDATA_CDMA, MMModemAnydataCdma)) +#define MM_MODEM_ANYDATA_CDMA_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_MODEM_ANYDATA_CDMA, MMModemAnydataCdmaClass)) +#define MM_IS_MODEM_ANYDATA_CDMA(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_MODEM_ANYDATA_CDMA)) +#define MM_IS_MODEM_ANYDATA_CDMA_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_MODEM_ANYDATA_CDMA)) +#define MM_MODEM_ANYDATA_CDMA_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_MODEM_ANYDATA_CDMA, MMModemAnydataCdmaClass)) + +typedef struct { + MMGenericCdma parent; +} MMModemAnydataCdma; + +typedef struct { + MMGenericCdmaClass parent; +} MMModemAnydataCdmaClass; + +GType mm_modem_anydata_cdma_get_type (void); + +MMModem *mm_modem_anydata_cdma_new (const char *device, + const char *driver, + const char *plugin, + gboolean evdo_rev0, + gboolean evdo_revA); + +#endif /* MM_MODEM_ANYDATA_CDMA_H */ diff --git a/plugins/mm-modem-gobi-gsm.c b/plugins/mm-modem-gobi-gsm.c new file mode 100644 index 0000000..7ea9f8f --- /dev/null +++ b/plugins/mm-modem-gobi-gsm.c @@ -0,0 +1,109 @@ +/* -*- 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. + */ + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> + +#include "mm-modem-gobi-gsm.h" +#include "mm-errors.h" +#include "mm-callback-info.h" +#include "mm-modem-gsm-card.h" + +static void modem_init (MMModem *modem_class); +static void modem_gsm_card_init (MMModemGsmCard *gsm_card_class); + +G_DEFINE_TYPE_EXTENDED (MMModemGobiGsm, mm_modem_gobi_gsm, MM_TYPE_GENERIC_GSM, 0, + G_IMPLEMENT_INTERFACE (MM_TYPE_MODEM, modem_init) + G_IMPLEMENT_INTERFACE (MM_TYPE_MODEM_GSM_CARD, modem_gsm_card_init)) + + +MMModem * +mm_modem_gobi_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_GOBI_GSM, + MM_MODEM_MASTER_DEVICE, device, + MM_MODEM_DRIVER, driver, + MM_MODEM_PLUGIN, plugin, + NULL)); +} + +/*****************************************************************************/ + +static void +get_string_done (MMSerialPort *port, + GString *response, + GError *error, + gpointer user_data) +{ + MMCallbackInfo *info = (MMCallbackInfo *) user_data; + + if (error && response && !strcmp (response->str, "ERROR")) { + info->error = g_error_new_literal (MM_MOBILE_ERROR, + MM_MOBILE_ERROR_SIM_NOT_INSERTED, + "Unable to read IMSI"); + } else if (error) + info->error = g_error_copy (error); + else + mm_callback_info_set_result (info, g_strdup (response->str), g_free); + + mm_callback_info_schedule (info); +} + +static void +get_imsi (MMModemGsmCard *modem, + MMModemStringFn callback, + gpointer user_data) +{ + MMSerialPort *primary; + 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); +} + +static void +modem_gsm_card_init (MMModemGsmCard *class) +{ + class->get_imsi = get_imsi; +} + +/*****************************************************************************/ + +static void +modem_init (MMModem *modem_class) +{ +} + +static void +mm_modem_gobi_gsm_init (MMModemGobiGsm *self) +{ +} + +static void +mm_modem_gobi_gsm_class_init (MMModemGobiGsmClass *klass) +{ +} + diff --git a/plugins/mm-modem-gobi-gsm.h b/plugins/mm-modem-gobi-gsm.h new file mode 100644 index 0000000..4bd262f --- /dev/null +++ b/plugins/mm-modem-gobi-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_GOBI_GSM_H +#define MM_MODEM_GOBI_GSM_H + +#include "mm-generic-gsm.h" + +#define MM_TYPE_MODEM_GOBI_GSM (mm_modem_gobi_gsm_get_type ()) +#define MM_MODEM_GOBI_GSM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_MODEM_GOBI_GSM, MMModemGobiGsm)) +#define MM_MODEM_GOBI_GSM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_MODEM_GOBI_GSM, MMModemGobiGsmClass)) +#define MM_IS_MODEM_GOBI_GSM(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_MODEM_GOBI_GSM)) +#define MM_IS_MODEM_GOBI_GSM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_MODEM_GOBI_GSM)) +#define MM_MODEM_GOBI_GSM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_MODEM_GOBI_GSM, MMModemGobiGsmClass)) + +typedef struct { + MMGenericGsm parent; +} MMModemGobiGsm; + +typedef struct { + MMGenericGsmClass parent; +} MMModemGobiGsmClass; + +GType mm_modem_gobi_gsm_get_type (void); + +MMModem *mm_modem_gobi_gsm_new (const char *device, + const char *driver, + const char *plugin_name); + +#endif /* MM_MODEM_GOBI_GSM_H */ diff --git a/plugins/mm-modem-hso.c b/plugins/mm-modem-hso.c new file mode 100644 index 0000000..f1295e2 --- /dev/null +++ b/plugins/mm-modem-hso.c @@ -0,0 +1,743 @@ +/* -*- 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. + */ + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <errno.h> +#include <stdlib.h> +#include <arpa/inet.h> +#include <dbus/dbus-glib.h> + +#define G_UDEV_API_IS_SUBJECT_TO_CHANGE +#include <gudev/gudev.h> + +#include "mm-modem-hso.h" +#include "mm-modem-simple.h" +#include "mm-serial-parsers.h" +#include "mm-errors.h" +#include "mm-callback-info.h" + +static void impl_hso_authenticate (MMModemHso *self, + const char *username, + const char *password, + DBusGMethodInvocation *context); + +#include "mm-modem-gsm-hso-glue.h" + +static void modem_init (MMModem *modem_class); +static void modem_simple_init (MMModemSimple *simple_class); + +G_DEFINE_TYPE_EXTENDED (MMModemHso, mm_modem_hso, MM_TYPE_GENERIC_GSM, 0, + G_IMPLEMENT_INTERFACE (MM_TYPE_MODEM, modem_init) + G_IMPLEMENT_INTERFACE (MM_TYPE_MODEM_SIMPLE, modem_simple_init)) + +#define MM_MODEM_HSO_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), MM_TYPE_MODEM_HSO, MMModemHsoPrivate)) + +static void _internal_hso_modem_authenticate (MMModemHso *self, MMCallbackInfo *info); + +const char *auth_commands[] = { + "$QCPDPP", + /* Icera-based devices (GI0322/Quicksilver, iCON 505) don't implement + * $QCPDPP, but instead use _OPDPP with the same arguments. + */ + "_OPDPP", + NULL +}; + +typedef struct { + /* Pending connection attempt */ + MMCallbackInfo *connect_pending_data; + guint connect_pending_id; + + guint32 auth_idx; +} MMModemHsoPrivate; + +#define OWANDATA_TAG "_OWANDATA: " + +MMModem * +mm_modem_hso_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_HSO, + MM_MODEM_MASTER_DEVICE, device, + MM_MODEM_DRIVER, driver, + MM_MODEM_PLUGIN, plugin, + MM_MODEM_IP_METHOD, MM_MODEM_IP_METHOD_STATIC, + NULL)); +} + +#define IGNORE_ERRORS_TAG "ignore-errors" + +static void +hso_call_control_done (MMSerialPort *port, + GString *response, + GError *error, + gpointer user_data) +{ + MMCallbackInfo *info = (MMCallbackInfo *) user_data; + + if (error && !mm_callback_info_get_data (info, IGNORE_ERRORS_TAG)) + info->error = g_error_copy (error); + + mm_callback_info_schedule (info); +} + +static guint32 +hso_get_cid (MMModemHso *self) +{ + guint32 cid; + + cid = mm_generic_gsm_get_cid (MM_GENERIC_GSM (self)); + if (cid == 0) + cid = 1; + + return cid; +} + +static void +hso_call_control (MMModemHso *self, + gboolean activate, + gboolean ignore_errors, + MMModemFn callback, + gpointer user_data) +{ + MMCallbackInfo *info; + char *command; + MMSerialPort *primary; + + 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); +} + +static void +connect_pending_done (MMModemHso *self) +{ + MMModemHsoPrivate *priv = MM_MODEM_HSO_GET_PRIVATE (self); + + if (priv->connect_pending_data) { + mm_callback_info_schedule (priv->connect_pending_data); + priv->connect_pending_data = NULL; + } + + if (priv->connect_pending_id) { + g_source_remove (priv->connect_pending_id); + priv->connect_pending_id = 0; + } +} + +static gboolean +hso_connect_timed_out (gpointer data) +{ + MMModemHsoPrivate *priv = MM_MODEM_HSO_GET_PRIVATE (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)); + + return FALSE; +} + +static void +hso_enabled (MMModem *modem, + GError *error, + gpointer user_data) +{ + MMCallbackInfo *info = (MMCallbackInfo *) user_data; + + if (error) { + 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); + } +} + +static void +clear_old_context (MMModem *modem, + GError *error, + gpointer user_data) +{ + MMCallbackInfo *info = (MMCallbackInfo *) user_data; + + 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); + } +} + +static void +auth_done (MMSerialPort *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) { + 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); + } +} + +static void +_internal_hso_modem_authenticate (MMModemHso *self, MMCallbackInfo *info) +{ + 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"); + + 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 : ""); + + } + + mm_serial_port_queue_command (primary, command, 3, auth_done, info); + g_free (command); +} + +void +mm_hso_modem_authenticate (MMModemHso *self, + const char *username, + const char *password, + MMModemFn callback, + gpointer user_data) +{ + MMCallbackInfo *info; + + 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); + 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); + + _internal_hso_modem_authenticate (self, info); +} + +/*****************************************************************************/ + +static void +enable_done (MMModem *modem, GError *error, gpointer user_data) +{ + MMCallbackInfo *info = (MMCallbackInfo *) user_data; + + mm_generic_gsm_enable_complete (MM_GENERIC_GSM (modem), error, info); +} + +static void +parent_enable_done (MMModem *modem, GError *error, gpointer user_data) +{ + MMCallbackInfo *info = (MMCallbackInfo *) user_data; + MMGenericGsm *self = MM_GENERIC_GSM (modem); + + if (error) { + mm_generic_gsm_enable_complete (self, error, info); + return; + } + + /* HSO needs manual PIN checking */ + mm_generic_gsm_check_pin (self, enable_done, info); +} + +static void +enable (MMModem *modem, MMModemFn callback, gpointer user_data) +{ + MMModem *parent_modem_iface; + MMCallbackInfo *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); +} + +static void +parent_disable_done (MMModem *modem, 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 +disable_done (MMModem *modem, + GError *error, + gpointer user_data) +{ + MMCallbackInfo *info = (MMCallbackInfo *) user_data; + MMModem *parent_modem_iface; + + /* Do the normal disable stuff */ + parent_modem_iface = g_type_interface_peek_parent (MM_MODEM_GET_INTERFACE (info->modem)); + parent_modem_iface->disable (info->modem, parent_disable_done, info); +} + +static void +disable (MMModem *modem, + MMModemFn callback, + gpointer user_data) +{ + MMCallbackInfo *info; + + mm_generic_gsm_pending_registration_stop (MM_GENERIC_GSM (modem)); + + info = mm_callback_info_new (modem, callback, user_data); + + /* Kill any existing connection */ + hso_call_control (MM_MODEM_HSO (modem), FALSE, TRUE, disable_done, info); +} + +static void +do_connect (MMModem *modem, + const char *number, + MMModemFn callback, + gpointer user_data) +{ + MMCallbackInfo *info; + + info = mm_callback_info_new (modem, callback, user_data); + mm_callback_info_schedule (info); +} + + +static void +free_dns_array (gpointer data) +{ + g_array_free ((GArray *) data, TRUE); +} + +static void +ip4_config_invoke (MMCallbackInfo *info) +{ + MMModemIp4Fn callback = (MMModemIp4Fn) info->callback; + + callback (info->modem, + GPOINTER_TO_UINT (mm_callback_info_get_data (info, "ip4-address")), + (GArray *) mm_callback_info_get_data (info, "ip4-dns"), + info->error, info->user_data); +} + +static void +get_ip4_config_done (MMSerialPort *port, + GString *response, + GError *error, + gpointer user_data) +{ + MMCallbackInfo *info = (MMCallbackInfo *) user_data; + char **items, **iter; + GArray *dns_array; + int i; + guint32 tmp; + guint cid; + + if (error) { + info->error = g_error_copy (error); + goto out; + } else if (!g_str_has_prefix (response->str, OWANDATA_TAG)) { + info->error = g_error_new_literal (MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL, + "Retrieving failed: invalid response."); + goto out; + } + + cid = hso_get_cid (MM_MODEM_HSO (info->modem)); + dns_array = g_array_sized_new (FALSE, TRUE, sizeof (guint32), 2); + items = g_strsplit (response->str + strlen (OWANDATA_TAG), ", ", 0); + + for (iter = items, i = 0; *iter; iter++, i++) { + if (i == 0) { /* CID */ + long int num; + + errno = 0; + num = strtol (*iter, NULL, 10); + if (errno != 0 || num < 0 || (guint) 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); + break; + } + } else if (i == 1) { /* IP address */ + if (inet_pton (AF_INET, *iter, &tmp) > 0) + mm_callback_info_set_data (info, "ip4-address", GUINT_TO_POINTER (tmp), NULL); + } else if (i == 3) { /* DNS 1 */ + if (inet_pton (AF_INET, *iter, &tmp) > 0) + g_array_append_val (dns_array, tmp); + } else if (i == 4) { /* DNS 2 */ + if (inet_pton (AF_INET, *iter, &tmp) > 0) + g_array_append_val (dns_array, tmp); + } + } + + g_strfreev (items); + mm_callback_info_set_data (info, "ip4-dns", dns_array, free_dns_array); + + out: + mm_callback_info_schedule (info); +} + +static void +get_ip4_config (MMModem *modem, + MMModemIp4Fn callback, + gpointer user_data) +{ + MMCallbackInfo *info; + char *command; + MMSerialPort *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); + g_assert (primary); + mm_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) +{ + MMCallbackInfo *info; + MMSerialPort *primary; + + info = mm_callback_info_new (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_OWANCALL=1,0,0", 3, NULL, info); +} + +/*****************************************************************************/ + +static void +impl_hso_auth_done (MMModem *modem, + GError *error, + gpointer user_data) +{ + DBusGMethodInvocation *context = (DBusGMethodInvocation *) user_data; + + if (error) + dbus_g_method_return_error (context, error); + else + dbus_g_method_return (context); +} + +static void +impl_hso_authenticate (MMModemHso *self, + const char *username, + const char *password, + DBusGMethodInvocation *context) +{ + /* DBus doesn't support NULLs */ + if (username && strlen (username) == 0) + username = NULL; + if (password && strlen (password) == 0) + password = NULL; + + 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) +{ + GHashTable *properties = (GHashTable *) mm_callback_info_get_data (info, "simple-connect-properties"); + 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_state_machine (MMModem *modem, GError *error, gpointer user_data) +{ + 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; + } + + 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); +} + +static void +simple_connect (MMModemSimple *simple, + GHashTable *properties, + MMModemFn callback, + gpointer user_data) +{ + MMCallbackInfo *info; + + 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); + + simple_state_machine (MM_MODEM (simple), NULL, 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; + const char *sys[] = { "tty", "net", NULL }; + GUdevClient *client; + GUdevDevice *device = NULL; + MMPort *port = NULL; + const char *sysfs_path; + + client = g_udev_client_new (sys); + if (!client) { + g_set_error (error, 0, 0, "Could not get udev client."); + return FALSE; + } + + device = g_udev_client_query_by_subsystem_and_name (client, subsys, name); + if (!device) { + g_set_error (error, 0, 0, "Could not get udev device."); + goto out; + } + + sysfs_path = g_udev_device_get_sysfs_path (device); + if (!sysfs_path) { + g_set_error (error, 0, 0, "Could not get udev device sysfs path."); + goto out; + } + + if (!strcmp (subsys, "tty")) { + char *hsotype_path; + char *contents = NULL; + + hsotype_path = g_build_filename (sysfs_path, "hsotype", NULL); + if (g_file_get_contents (hsotype_path, &contents, NULL, NULL)) { + if (g_str_has_prefix (contents, "Control")) + ptype = MM_PORT_TYPE_PRIMARY; + else if (g_str_has_prefix (contents, "Application") || g_str_has_prefix (contents, "Application2")) + ptype = MM_PORT_TYPE_SECONDARY; + g_free (contents); + } + g_free (hsotype_path); + } + + port = mm_generic_gsm_grab_port (gsm, subsys, name, ptype, error); + if (!port) + goto out; + + if (MM_IS_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); + g_regex_unref (regex); + } + } + +out: + if (device) + g_object_unref (device); + g_object_unref (client); + return !!port; +} + +/*****************************************************************************/ + +static void +mm_modem_hso_init (MMModemHso *self) +{ +} + +static void +modem_simple_init (MMModemSimple *class) +{ + class->connect = simple_connect; +} + +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) +{ + /* Clear the pending connection if necessary */ + connect_pending_done (MM_MODEM_HSO (object)); + + G_OBJECT_CLASS (mm_modem_hso_parent_class)->finalize (object); +} + +static void +mm_modem_hso_class_init (MMModemHsoClass *klass) +{ + GObjectClass *object_class = G_OBJECT_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; +} + diff --git a/plugins/mm-modem-hso.h b/plugins/mm-modem-hso.h new file mode 100644 index 0000000..e2d7623 --- /dev/null +++ b/plugins/mm-modem-hso.h @@ -0,0 +1,49 @@ +/* -*- 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_HSO_H +#define MM_MODEM_HSO_H + +#include "mm-generic-gsm.h" + +#define MM_TYPE_MODEM_HSO (mm_modem_hso_get_type ()) +#define MM_MODEM_HSO(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_MODEM_HSO, MMModemHso)) +#define MM_MODEM_HSO_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_MODEM_HSO, MMModemHsoClass)) +#define MM_IS_MODEM_HSO(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_MODEM_HSO)) +#define MM_IS_MODEM_HSO_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_MODEM_HSO)) +#define MM_MODEM_HSO_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_MODEM_HSO, MMModemHsoClass)) + +typedef struct { + MMGenericGsm parent; +} MMModemHso; + +typedef struct { + MMGenericGsmClass parent; +} MMModemHsoClass; + +GType mm_modem_hso_get_type (void); + +MMModem *mm_modem_hso_new (const char *device, + const char *driver, + const char *plugin); + +void mm_hso_modem_authenticate (MMModemHso *self, + const char *username, + const char *password, + MMModemFn callback, + gpointer user_data); + +#endif /* MM_MODEM_HSO_H */ diff --git a/plugins/mm-modem-huawei-cdma.c b/plugins/mm-modem-huawei-cdma.c new file mode 100644 index 0000000..3b63a48 --- /dev/null +++ b/plugins/mm-modem-huawei-cdma.c @@ -0,0 +1,316 @@ +/* -*- 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. + */ + +#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-cdma.h" +#include "mm-errors.h" +#include "mm-callback-info.h" +#include "mm-serial-port.h" +#include "mm-serial-parsers.h" + +static void modem_init (MMModem *modem_class); + +G_DEFINE_TYPE_EXTENDED (MMModemHuaweiCdma, mm_modem_huawei_cdma, MM_TYPE_GENERIC_CDMA, 0, + G_IMPLEMENT_INTERFACE (MM_TYPE_MODEM, modem_init)) + + +MMModem * +mm_modem_huawei_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_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, + NULL)); +} + +/* Unsolicited message handlers */ + +static gint +parse_quality (const char *str, const char *detail) +{ + long int quality = 0; + + errno = 0; + quality = strtol (str, NULL, 10); + if (errno == 0) { + quality = CLAMP (quality, 0, 100); + g_debug ("%s: %ld", detail, quality); + return (gint) quality; + } + return -1; +} + +static void +handle_1x_quality_change (MMSerialPort *port, + GMatchInfo *match_info, + gpointer user_data) +{ + MMModemHuaweiCdma *self = MM_MODEM_HUAWEI_CDMA (user_data); + char *str; + gint quality; + + str = g_match_info_fetch (match_info, 1); + quality = parse_quality (str, "1X signal quality"); + g_free (str); + + if (quality >= 0) + mm_generic_cdma_update_cdma1x_quality (MM_GENERIC_CDMA (self), (guint32) quality); +} + +static void +handle_evdo_quality_change (MMSerialPort *port, + GMatchInfo *match_info, + gpointer user_data) +{ + MMModemHuaweiCdma *self = MM_MODEM_HUAWEI_CDMA (user_data); + char *str; + gint quality; + + str = g_match_info_fetch (match_info, 1); + quality = parse_quality (str, "EVDO signal quality"); + g_free (str); + + if (quality >= 0) + mm_generic_cdma_update_evdo_quality (MM_GENERIC_CDMA (self), (guint32) quality); +} + +/*****************************************************************************/ + +static const char * +strip_response (const char *resp, const char *cmd) +{ + const char *p = resp; + + if (p) { + if (!strncmp (p, cmd, strlen (cmd))) + p += strlen (cmd); + while (*p == ' ') + p++; + } + return p; +} + +static gboolean +uint_from_match_item (GMatchInfo *match_info, guint32 num, guint32 *val) +{ + long int tmp; + char *str; + gboolean success = FALSE; + + str = g_match_info_fetch (match_info, num); + g_return_val_if_fail (str != NULL, FALSE); + + errno = 0; + tmp = strtol (str, NULL, 10); + if (errno == 0 && tmp >= 0 && tmp <= G_MAXUINT) { + *val = (guint32) tmp; + success = TRUE; + } + g_free (str); + return success; +} + +static void +sysinfo_done (MMSerialPort *port, + GString *response, + GError *error, + gpointer user_data) +{ + MMCallbackInfo *info = (MMCallbackInfo *) user_data; + 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; + } + + reply = strip_response (response->str, "^SYSINFO:"); + + /* Format is "<srv_status>,<srv_domain>,<roam_status>,<sys_mode>,<sim_state>" */ + 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)."); + goto done; + } + + g_regex_match (r, reply, 0, &match_info); + if (g_match_info_get_match_count (match_info) >= 5) { + MMModemCdmaRegistrationState reg_state; + guint32 val = 0; + + /* At this point the generic code already knows we've been registered */ + reg_state = MM_MODEM_CDMA_REGISTRATION_STATE_REGISTERED; + + if (uint_from_match_item (match_info, 1, &val)) { + if (val == 2) { + /* Service available, check roaming state */ + val = 0; + if (uint_from_match_item (match_info, 3, &val)) { + if (val == 0) + reg_state = MM_MODEM_CDMA_REGISTRATION_STATE_HOME; + else if (val == 1) + reg_state = MM_MODEM_CDMA_REGISTRATION_STATE_ROAMING; + } + } + } + + /* Check service type */ + val = 0; + if (uint_from_match_item (match_info, 4, &val)) { + if (val == 2) + mm_generic_cdma_query_reg_state_set_callback_1x_state (info, reg_state); + else if (val == 4) + mm_generic_cdma_query_reg_state_set_callback_evdo_state (info, reg_state); + else if (val == 8) { + 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); + } + } else { + /* 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; + } + +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."); + } + + mm_callback_info_schedule (info); +} + +static void +query_registration_state (MMGenericCdma *cdma, + 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); + + 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; + } + + mm_serial_port_queue_command (port, "^SYSINFO", 3, sysinfo_done, info); +} + +/*****************************************************************************/ + +static gboolean +grab_port (MMModem *modem, + const char *subsys, + const char *name, + MMPortType suggested_type, + gpointer user_data, + GError **error) +{ + MMPort *port = NULL; + 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)) { + 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); + g_regex_unref (regex); + + g_object_get (G_OBJECT (modem), + MM_GENERIC_CDMA_EVDO_REV0, &evdo0, + MM_GENERIC_CDMA_EVDO_REVA, &evdoA, + NULL); + + 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); + g_regex_unref (regex); + } + } + + return !!port; +} + +/*****************************************************************************/ + +static void +modem_init (MMModem *modem_class) +{ + modem_class->grab_port = grab_port; +} + +static void +mm_modem_huawei_cdma_init (MMModemHuaweiCdma *self) +{ +} + +static void +mm_modem_huawei_cdma_class_init (MMModemHuaweiCdmaClass *klass) +{ + MMGenericCdmaClass *cdma_class = MM_GENERIC_CDMA_CLASS (klass); + + mm_modem_huawei_cdma_parent_class = g_type_class_peek_parent (klass); + + cdma_class->query_registration_state = query_registration_state; +} + diff --git a/plugins/mm-modem-huawei-cdma.h b/plugins/mm-modem-huawei-cdma.h new file mode 100644 index 0000000..738f2d9 --- /dev/null +++ b/plugins/mm-modem-huawei-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 Red Hat, Inc. + */ + +#ifndef MM_MODEM_HUAWEI_CDMA_H +#define MM_MODEM_HUAWEI_CDMA_H + +#include "mm-generic-cdma.h" + +#define MM_TYPE_MODEM_HUAWEI_CDMA (mm_modem_huawei_cdma_get_type ()) +#define MM_MODEM_HUAWEI_CDMA(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_MODEM_HUAWEI_CDMA, MMModemHuaweiCdma)) +#define MM_MODEM_HUAWEI_CDMA_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_MODEM_HUAWEI_CDMA, MMModemHuaweiCdmaClass)) +#define MM_IS_MODEM_HUAWEI_CDMA(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_MODEM_HUAWEI_CDMA)) +#define MM_IS_MODEM_HUAWEI_CDMA_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_MODEM_HUAWEI_CDMA)) +#define MM_MODEM_HUAWEI_CDMA_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_MODEM_HUAWEI_CDMA, MMModemHuaweiCdmaClass)) + +typedef struct { + MMGenericCdma parent; +} MMModemHuaweiCdma; + +typedef struct { + MMGenericCdmaClass parent; +} MMModemHuaweiCdmaClass; + +GType mm_modem_huawei_cdma_get_type (void); + +MMModem *mm_modem_huawei_cdma_new (const char *device, + const char *driver, + const char *plugin, + gboolean evdo_rev0, + gboolean evdo_revA); + +#endif /* MM_MODEM_HUAWEI_CDMA_H */ diff --git a/plugins/mm-modem-huawei-gsm.c b/plugins/mm-modem-huawei-gsm.c new file mode 100644 index 0000000..d450f25 --- /dev/null +++ b/plugins/mm-modem-huawei-gsm.c @@ -0,0 +1,621 @@ +/* -*- 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. + */ + +#include <stdlib.h> +#include <stdio.h> +#include <string.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-errors.h" +#include "mm-callback-info.h" +#include "mm-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); + +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)) + + +#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; +} MMModemHuaweiGsmPrivate; + +MMModem * +mm_modem_huawei_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_HUAWEI_GSM, + MM_MODEM_MASTER_DEVICE, device, + MM_MODEM_DRIVER, driver, + MM_MODEM_PLUGIN, plugin, + NULL)); +} + +static gboolean +parse_syscfg (MMModemHuaweiGsm *self, + const char *reply, + int *mode_a, + int *mode_b, + guint32 *band, + int *unknown1, + int *unknown2) +{ + 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); + + /* Network mode */ + if (*mode_a == 2 && *mode_b == 1) + priv->mode = MM_MODEM_GSM_MODE_2G_PREFERRED; + else if (*mode_a == 2 && *mode_b == 2) + priv->mode = MM_MODEM_GSM_MODE_3G_PREFERRED; + else if (*mode_a == 13 && *mode_b == 1) + priv->mode = MM_MODEM_GSM_MODE_2G_ONLY; + else if (*mode_a == 14 && *mode_b == 2) + priv->mode = MM_MODEM_GSM_MODE_3G_ONLY; + + /* 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; + + return TRUE; + } + + return FALSE; +} + +static void +set_network_mode_done (MMSerialPort *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, + MMModemFn callback, + gpointer user_data) +{ + MMCallbackInfo *info; + MMSerialPort *primary; + + 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_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); + return; + default: + info->error = g_error_new_literal (MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL, "Invalid mode."); + break; + } + + mm_callback_info_schedule (info); +} + +static void +get_network_mode_done (MMSerialPort *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->mode), NULL); + + mm_callback_info_schedule (info); +} + +static void +get_network_mode (MMModemGsmNetwork *modem, + MMModemUIntFn callback, + gpointer user_data) +{ + MMModemHuaweiGsmPrivate *priv = MM_MODEM_HUAWEI_GSM_GET_PRIVATE (modem); + + 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 (modem), callback, user_data); + mm_callback_info_set_result (info, GUINT_TO_POINTER (priv->mode), NULL); + 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); + } +} + +static void +set_band_done (MMSerialPort *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->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; + + 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); + return; + default: + info->error = g_error_new_literal (MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL, "Invalid band."); + break; + } + + mm_callback_info_schedule (info); +} + +static void +get_band_done (MMSerialPort *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); + + mm_callback_info_schedule (info); +} + +static void +get_band (MMModemGsmNetwork *modem, + MMModemUIntFn callback, + gpointer user_data) +{ + MMModemHuaweiGsmPrivate *priv = MM_MODEM_HUAWEI_GSM_GET_PRIVATE (modem); + MMSerialPort *primary; + + if (priv->band != MM_MODEM_GSM_BAND_ANY) { + /* have cached mode (from an unsolicited message). Use that */ + MMCallbackInfo *info; + + info = mm_callback_info_uint_new (MM_MODEM (modem), callback, user_data); + mm_callback_info_set_result (info, GUINT_TO_POINTER (priv->band), NULL); + mm_callback_info_schedule (info); + } else { + /* Get it from modem */ + MMCallbackInfo *info; + + 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); + } +} + +static void +get_signal_quality (MMModemGsmNetwork *modem, + MMModemUIntFn callback, + gpointer user_data) +{ + MMModemHuaweiGsmPrivate *priv = MM_MODEM_HUAWEI_GSM_GET_PRIVATE (modem); + + if (priv->signal_quality) { + /* have cached signal quality (from an unsolicited message). Use that */ + MMCallbackInfo *info; + + 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); + mm_callback_info_schedule (info); + } else { + /* Use the generic implementation */ + MMModemGsmNetwork *parent_gsm_network_iface; + + 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); + } +} + +/* Unsolicited message handlers */ + +static void +handle_signal_quality_change (MMSerialPort *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; + + 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 = quality * 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); +} + +static void +handle_mode_change (MMSerialPort *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 a; + int b; + + str = g_match_info_fetch (match_info, 1); + a = atoi (str); + g_free (str); + + str = g_match_info_fetch (match_info, 2); + 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; + 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); +} + +static void +handle_status_change (MMSerialPort *port, + GMatchInfo *match_info, + gpointer user_data) +{ + char *str; + int n1, n2, n3, n4, n5, n6, n7; + + str = g_match_info_fetch (match_info, 1); + if (sscanf (str, "%x,%x,%x,%x,%x,%x,%x", &n1, &n2, &n3, &n4, &n5, &n6, &n7)) { + g_debug ("Duration: %d Up: %d Kbps Down: %d Kbps Total: %d Total: %d\n", + n1, n2 * 8 / 1000, n3 * 8 / 1000, n4 / 1024, n5 / 1024); + } + g_free (str); +} + +/*****************************************************************************/ + +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; + const char *sys[] = { "tty", NULL }; + GUdevClient *client; + GUdevDevice *device = NULL; + MMPort *port = NULL; + int usbif; + + client = g_udev_client_new (sys); + if (!client) { + g_set_error (error, 0, 0, "Could not get udev client."); + return FALSE; + } + + device = g_udev_client_query_by_subsystem_and_name (client, subsys, name); + if (!device) { + g_set_error (error, 0, 0, "Could not get udev device."); + goto out; + } + + usbif = g_udev_device_get_property_as_int (device, "ID_USB_INTERFACE_NUM"); + if (usbif < 0) { + g_set_error (error, 0, 0, "Could not get USB device interface number."); + goto out; + } + + if (usbif == 0) { + if (!mm_generic_gsm_get_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)) + 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; + + mm_generic_gsm_set_unsolicited_registration (MM_GENERIC_GSM (modem), TRUE); + + 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\\^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\\^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\\^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); + } + } + +out: + if (device) + g_object_unref (device); + g_object_unref (client); + return !!port; +} + +/*****************************************************************************/ + +static void +modem_init (MMModem *modem_class) +{ + modem_class->grab_port = grab_port; +} + +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 +mm_modem_huawei_gsm_init (MMModemHuaweiGsm *self) +{ +} + +static void +mm_modem_huawei_gsm_class_init (MMModemHuaweiGsmClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + mm_modem_huawei_gsm_parent_class = g_type_class_peek_parent (klass); + g_type_class_add_private (object_class, sizeof (MMModemHuaweiGsmPrivate)); +} + diff --git a/plugins/mm-modem-huawei-gsm.h b/plugins/mm-modem-huawei-gsm.h new file mode 100644 index 0000000..9c2ec34 --- /dev/null +++ b/plugins/mm-modem-huawei-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_HUAWEI_GSM_H +#define MM_MODEM_HUAWEI_GSM_H + +#include "mm-generic-gsm.h" + +#define MM_TYPE_MODEM_HUAWEI_GSM (mm_modem_huawei_gsm_get_type ()) +#define MM_MODEM_HUAWEI_GSM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_MODEM_HUAWEI_GSM, MMModemHuaweiGsm)) +#define MM_MODEM_HUAWEI_GSM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_MODEM_HUAWEI_GSM, MMModemHuaweiGsmClass)) +#define MM_IS_MODEM_HUAWEI_GSM(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_MODEM_HUAWEI_GSM)) +#define MM_IS_MODEM_HUAWEI_GSM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_MODEM_HUAWEI_GSM)) +#define MM_MODEM_HUAWEI_GSM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_MODEM_HUAWEI_GSM, MMModemHuaweiGsmClass)) + +typedef struct { + MMGenericGsm parent; +} MMModemHuaweiGsm; + +typedef struct { + MMGenericGsmClass parent; +} MMModemHuaweiGsmClass; + +GType mm_modem_huawei_gsm_get_type (void); + +MMModem *mm_modem_huawei_gsm_new (const char *device, + const char *driver, + const char *plugin); + +#endif /* MM_MODEM_HUAWEI_GSM_H */ diff --git a/plugins/mm-modem-mbm.c b/plugins/mm-modem-mbm.c new file mode 100644 index 0000000..686b35c --- /dev/null +++ b/plugins/mm-modem-mbm.c @@ -0,0 +1,830 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * Copyright (C) 2008 Ericsson AB + * + * 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> + * + * 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. + * + * 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> +#include <stdio.h> +#include <string.h> +#include <unistd.h> + +#include "mm-modem-mbm.h" +#include "mm-modem-simple.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); + +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)) + +#define MM_MODEM_MBM_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), MM_TYPE_MODEM_MBM, MMModemMbmPrivate)) + +#define MBM_E2NAP_DISCONNECTED 0 +#define MBM_E2NAP_CONNECTED 1 +#define MBM_E2NAP_CONNECTING 2 + +#define MBM_SIGNAL_INDICATOR 2 + +#define MBM_NETWORK_MODE_ANY 1 +#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; + char *network_device; + MMCallbackInfo *pending_connect_info; + int account_index; + int network_mode; + const char *username; + const char *password; +} MMModemMbmPrivate; + +enum { + PROP_0, + PROP_NETWORK_DEVICE, + + LAST_PROP +}; + +static void +mbm_modem_authenticate (MMModemMbm *self, + const char *username, + 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, + 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_MBM, + MM_MODEM_MASTER_DEVICE, device, + MM_MODEM_DRIVER, driver, + MM_MODEM_PLUGIN, plugin, + MM_MODEM_IP_METHOD, MM_MODEM_IP_METHOD_DHCP, + NULL)); +} + +/*****************************************************************************/ +/* Gsm network class override functions */ +/*****************************************************************************/ + +typedef struct { + MMModemGsmNetwork *modem; + const char *network_id; + MMModemFn callback; + gpointer user_data; +} RegisterData; + +static gboolean +register_done (gpointer user_data) +{ + RegisterData *reg_data = 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, + reg_data->callback, + reg_data->user_data); + return FALSE; +} + +static void +do_register (MMModemGsmNetwork *modem, + const char *network_id, + MMModemFn callback, + gpointer user_data) +{ + MMModemMbm *self = MM_MODEM_MBM (modem); + MMModemMbmPrivate *priv = MM_MODEM_MBM_GET_PRIVATE (self); + RegisterData *reg_data; + + reg_data = g_malloc0 (sizeof(RegisterData)); + reg_data->modem = modem; + reg_data->network_id = network_id; + reg_data->callback = callback; + reg_data->user_data = user_data; + + /* wait so sim pin is done */ + if (priv->reg_id) + g_source_remove (priv->reg_id); + priv->reg_id = g_timeout_add_full (G_PRIORITY_DEFAULT, 500, + register_done, reg_data, + g_free); +} + +static int +mbm_parse_network_mode (MMModemGsmMode 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: + return MBM_NETWORK_MODE_ANY; + case MM_MODEM_GSM_MODE_GPRS: + case MM_MODEM_GSM_MODE_EDGE: + case MM_MODEM_GSM_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: + return MBM_NETWORK_MODE_3G; + default: + return MBM_NETWORK_MODE_ANY; + } +} + +static void +mbm_set_network_mode_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); +} + +static void +set_network_mode (MMModemGsmNetwork *modem, + MMModemGsmMode mode, + MMModemFn callback, + gpointer user_data) +{ + MMCallbackInfo *info; + char *command; + MMSerialPort *primary; + + 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); + + 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); + g_free (command); +} + +static void +get_network_mode_done (MMSerialPort *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) { + 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; + + 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); + + mm_callback_info_set_result (info, GUINT_TO_POINTER (mm_mode), NULL); + parsed = TRUE; + } + +done: + 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) +{ + 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, "*ERINFO?", 3, get_network_mode_done, info); +} + +/*****************************************************************************/ +/* Simple Modem class override functions */ +/*****************************************************************************/ + +static void +simple_connect (MMModemSimple *simple, + GHashTable *properties, + MMModemFn callback, + gpointer user_data) +{ + 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); +} + +/*****************************************************************************/ +/* Modem class override functions */ +/*****************************************************************************/ + +static void +mbm_enable_done (MMSerialPort *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); +} + +static void +mbm_enap0_done (MMSerialPort *port, + GString *response, + GError *error, + gpointer user_data) +{ + MMCallbackInfo *info = user_data; + MMModemMbmPrivate *priv = MM_MODEM_MBM_GET_PRIVATE (info->modem); + char *command; + + if (!priv->network_mode) + 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); + g_free (command); +} + +static void +mbm_init_done (MMSerialPort *port, + GString *response, + GError *error, + gpointer user_data) +{ + MMCallbackInfo *info = user_data; + MMModemMbmPrivate *priv = MM_MODEM_MBM_GET_PRIVATE (info->modem); + + if (error) { + mm_generic_gsm_enable_complete (MM_GENERIC_GSM (info->modem), error, info); + return; + } + + if (!priv->network_mode) + priv->network_mode = MBM_NETWORK_MODE_ANY; + + mm_serial_port_queue_command (port, "*ENAP=0", 3, mbm_enap0_done, info); +} + +static void +do_init (MMSerialPort *port, MMCallbackInfo *info) +{ + mm_serial_port_queue_command (port, "&F E0 V1 X4 &C1 +CMEE=1", 3, mbm_init_done, info); +} + +static void +mbm_emrdy_done (MMSerialPort *port, + GString *response, + GError *error, + gpointer user_data) +{ + 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) { + g_warning ("%s: timed out waiting for EMRDY response.", __func__); + } else + priv->have_emrdy = TRUE; + + do_init (port, info); +} + +static void +do_enable (MMGenericGsm *self, MMModemFn callback, gpointer user_data) +{ + MMModemMbmPrivate *priv = MM_MODEM_MBM_GET_PRIVATE (self); + MMCallbackInfo *info; + MMSerialPort *primary; + + info = mm_callback_info_new (MM_MODEM (self), callback, user_data); + + primary = mm_generic_gsm_get_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); +} + +typedef struct { + MMModem *modem; + MMModemFn callback; + gpointer user_data; +} DisableInfo; + +static void +disable_creg_cmer_done (MMSerialPort *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) +{ + MMSerialPort *primary; + DisableInfo *info; + + info = g_malloc0 (sizeof (DisableInfo)); + info->callback = callback; + info->user_data = user_data; + info->modem = modem; + + primary = mm_generic_gsm_get_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); +} + +static void +do_connect (MMModem *modem, + const char *number, + MMModemFn callback, + gpointer user_data) +{ + MMCallbackInfo *info; + MMModemMbmPrivate *priv = MM_MODEM_MBM_GET_PRIVATE (modem); + + mm_modem_set_state (modem, MM_MODEM_STATE_CONNECTING, MM_MODEM_STATE_REASON_NONE); + + info = mm_callback_info_new (modem, callback, user_data); + priv->pending_connect_info = info; + + mbm_modem_authenticate (MM_MODEM_MBM (modem), priv->username, priv->password, info); +} + +static void +disconnect (MMModem *modem, + MMModemFn callback, + gpointer user_data) +{ + MMCallbackInfo *info; + MMSerialPort *primary; + + mm_modem_set_state (modem, MM_MODEM_STATE_DISCONNECTING, MM_MODEM_STATE_REASON_NONE); + + primary = mm_generic_gsm_get_port (MM_GENERIC_GSM (modem), MM_PORT_TYPE_PRIMARY); + g_assert (primary); + mm_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); + + info = mm_callback_info_new (modem, callback, user_data); + mm_callback_info_schedule (info); +} + +/*****************************************************************************/ + +static void +mbm_emrdy_received (MMSerialPort *port, + GMatchInfo *info, + gpointer user_data) +{ + MM_MODEM_MBM_GET_PRIVATE (user_data)->have_emrdy = TRUE; +} + +static void +mbm_pacsp_received (MMSerialPort *port, + GMatchInfo *info, + gpointer user_data) +{ + return; +} + +static void +mbm_ciev_received (MMSerialPort *port, + GMatchInfo *info, + gpointer user_data) +{ + int quality = 0, ind = 0; + const char *str; + + str = g_match_info_fetch (info, 1); + if (str) + ind = atoi (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); + } + } +} + +static void +mbm_do_connect_done (MMModemMbm *self) +{ + MMModemMbmPrivate *priv = MM_MODEM_MBM_GET_PRIVATE (self); + + if (priv->pending_connect_info) { + mm_generic_gsm_connect_complete (MM_GENERIC_GSM (self), NULL, priv->pending_connect_info); + priv->pending_connect_info = NULL; + } +} + +static void +mbm_e2nap_received (MMSerialPort *port, + GMatchInfo *info, + gpointer user_data) +{ + int state = 0; + const 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)); + } else if (MBM_E2NAP_CONNECTING == state) + g_debug("%s, connecting", __func__); + else { + /* Should not happen */ + g_debug("%s, undefined e2nap status",__FUNCTION__); + g_assert_not_reached (); + } +} + +static void +enap_poll_response (MMSerialPort *port, + GString *response, + GError *error, + gpointer user_data) +{ + MMCallbackInfo *info = (MMCallbackInfo *) user_data; + guint state; + guint count; + + g_assert (info); + + count = GPOINTER_TO_UINT (mm_callback_info_get_data (info, "mbm-enap-poll-count")); + + if (sscanf (response->str, "*ENAP: %d", &state) == 1 && state == 1) { + /* Success! Connected... */ + mm_generic_gsm_connect_complete (MM_GENERIC_GSM (info->modem), NULL, info); + return; + } + + mm_callback_info_set_data (info, "mbm-enap-poll-count", GUINT_TO_POINTER (++count), NULL); + + /* lets give it about 50 seconds */ + if (count > 50) { + GError *poll_error; + + poll_error = mm_modem_connect_error_for_code (MM_MODEM_CONNECT_ERROR_BUSY); + mm_generic_gsm_connect_complete (MM_GENERIC_GSM (info->modem), poll_error, info); + g_error_free (poll_error); + } +} + +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); + + g_assert (port); + + mm_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) +{ + MMCallbackInfo *info = (MMCallbackInfo *) user_data; + guint tid; + + if (error) { + mm_generic_gsm_connect_complete (MM_GENERIC_GSM (info->modem), error, info); + return; + } + + 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, + GError *error, + gpointer user_data) +{ + MMCallbackInfo *info = (MMCallbackInfo *) user_data; + MMGenericGsm *modem = MM_GENERIC_GSM (info->modem); + char *command; + guint32 cid; + + if (error) { + mm_generic_gsm_connect_complete (modem, error, info); + return; + } + + cid = mm_generic_gsm_get_cid (modem); + command = g_strdup_printf ("AT*ENAP=1,%d", cid); + mm_serial_port_queue_command (port, command, 3, enap_done, user_data); + g_free (command); +} + +static void +mbm_modem_authenticate (MMModemMbm *self, + const char *username, + const char *password, + gpointer user_data) +{ + MMSerialPort *primary; + + primary = mm_generic_gsm_get_port (MM_GENERIC_GSM (self), MM_PORT_TYPE_PRIMARY); + g_assert (primary); + + if (username || password) { + char *command; + + command = g_strdup_printf ("*EIAAUW=%d,1,\"%s\",\"%s\"", + mm_generic_gsm_get_cid (MM_GENERIC_GSM (self)), + username ? username : "", + password ? password : ""); + + mm_serial_port_queue_command (primary, command, 3, mbm_auth_done, user_data); + g_free (command); + } else + mbm_auth_done (primary, NULL, NULL, user_data); +} + +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 uint +mbm_simple_get_uint_property (GHashTable *properties, const char *name, GError **error) +{ + GValue *value; + + value = (GValue *) g_hash_table_lookup (properties, name); + if (!value) + return 0; + + if (G_VALUE_HOLDS_UINT (value)) + return g_value_get_uint (value); + + 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)); + + return 0; +} + +/*****************************************************************************/ + +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 (!strcmp (subsys, "tty")) { + 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; + } + + port = mm_generic_gsm_grab_port (gsm, subsys, name, ptype, error); + if (port && MM_IS_SERIAL_PORT (port) && (ptype == MM_PORT_TYPE_PRIMARY)) { + GRegex *regex; + + mm_generic_gsm_set_unsolicited_registration (MM_GENERIC_GSM (modem), TRUE); + + 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); + + 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); + 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); + 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); + 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); + 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); + g_regex_unref (regex); + } + + return TRUE; +} + +/*****************************************************************************/ + +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 +modem_simple_init (MMModemSimple *class) +{ + class->connect = simple_connect; +} + +static void +modem_init (MMModem *modem_class) +{ + modem_class->grab_port = grab_port; + modem_class->disable = disable; + modem_class->connect = do_connect; + modem_class->disconnect = disconnect; +} + +static void +mm_modem_mbm_init (MMModemMbm *self) +{ +} + +static void +finalize (GObject *object) +{ + MMModemMbmPrivate *priv = MM_MODEM_MBM_GET_PRIVATE (object); + + if (priv->reg_id) + g_source_remove (priv->reg_id); + + g_free (priv->network_device); + + G_OBJECT_CLASS (mm_modem_mbm_parent_class)->finalize (object); +} + +static void +mm_modem_mbm_class_init (MMModemMbmClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + MMGenericGsmClass *gsm_class = MM_GENERIC_GSM_CLASS (klass); + + mm_modem_mbm_parent_class = g_type_class_peek_parent (klass); + g_type_class_add_private (object_class, sizeof (MMModemMbmPrivate)); + + /* Virtual methods */ + object_class->finalize = finalize; + + gsm_class->do_enable = do_enable; +} + diff --git a/plugins/mm-modem-mbm.h b/plugins/mm-modem-mbm.h new file mode 100644 index 0000000..8756e47 --- /dev/null +++ b/plugins/mm-modem-mbm.h @@ -0,0 +1,52 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * Copyright (C) 2008 Ericsson AB + * + * 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> + * + * 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. + * + * 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 +#define MM_MODEM_MBM_H + +#include "mm-generic-gsm.h" + +#define MM_TYPE_MODEM_MBM (mm_modem_mbm_get_type ()) +#define MM_MODEM_MBM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_MODEM_MBM, MMModemMbm)) +#define MM_MODEM_MBM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_MODEM_MBM, MMModemMbmClass)) +#define MM_IS_MODEM_MBM(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_MODEM_MBM)) +#define MM_IS_MODEM_MBM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_MODEM_MBM)) +#define MM_MODEM_MBM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_MODEM_MBM, MMModemMbmClass)) + +typedef struct { + MMGenericGsm parent; +} MMModemMbm; + +typedef struct { + MMGenericGsmClass parent; +} MMModemMbmClass; + +GType mm_modem_mbm_get_type (void); + +MMModem *mm_modem_mbm_new (const char *device, + const char *driver, + const char *plugin_name); + +#endif /* MM_MODEM_MBM_H */ diff --git a/plugins/mm-modem-moto-c-gsm.c b/plugins/mm-modem-moto-c-gsm.c new file mode 100644 index 0000000..5910ff2 --- /dev/null +++ b/plugins/mm-modem-moto-c-gsm.c @@ -0,0 +1,129 @@ +/* -*- 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. + */ + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> + +#include "mm-modem-moto-c-gsm.h" +#include "mm-errors.h" +#include "mm-callback-info.h" +#include "mm-modem-gsm-card.h" + +static void modem_init (MMModem *modem_class); +static void modem_gsm_card_init (MMModemGsmCard *gsm_card_class); + +G_DEFINE_TYPE_EXTENDED (MMModemMotoCGsm, mm_modem_moto_c_gsm, MM_TYPE_GENERIC_GSM, 0, + G_IMPLEMENT_INTERFACE (MM_TYPE_MODEM, modem_init) + G_IMPLEMENT_INTERFACE (MM_TYPE_MODEM_GSM_CARD, modem_gsm_card_init)) + + +MMModem * +mm_modem_moto_c_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_MOTO_C_GSM, + MM_MODEM_MASTER_DEVICE, device, + MM_MODEM_DRIVER, driver, + MM_MODEM_PLUGIN, plugin, + NULL)); +} + +/*****************************************************************************/ + +static void +modem_init (MMModem *modem_class) +{ +} + +/*****************************************************************************/ + +static void +get_imei (MMModemGsmCard *modem, + MMModemStringFn callback, + gpointer user_data) +{ + MMCallbackInfo *info; + + info = mm_callback_info_string_new (MM_MODEM (modem), callback, user_data); + info->error = g_error_new_literal (MM_MODEM_ERROR, MM_MODEM_ERROR_OPERATION_NOT_SUPPORTED, + "Operation not supported"); + mm_callback_info_schedule (info); +} + +static void +modem_gsm_card_init (MMModemGsmCard *class) +{ + class->get_imei = get_imei; +} + +/*****************************************************************************/ + +static void +mm_modem_moto_c_gsm_init (MMModemMotoCGsm *self) +{ +} + +static void +get_property (GObject *object, guint prop_id, + GValue *value, GParamSpec *pspec) +{ + + /* These devices just don't implement AT+CFUN */ + + switch (prop_id) { + case MM_GENERIC_GSM_PROP_POWER_UP_CMD: + g_value_set_string (value, ""); + break; + case MM_GENERIC_GSM_PROP_POWER_DOWN_CMD: + g_value_set_string (value, ""); + break; + default: + break; + } +} + +static void +set_property (GObject *object, guint prop_id, + const GValue *value, GParamSpec *pspec) +{ +} + +static void +mm_modem_moto_c_gsm_class_init (MMModemMotoCGsmClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + mm_modem_moto_c_gsm_parent_class = g_type_class_peek_parent (klass); + + object_class->get_property = get_property; + object_class->set_property = set_property; + + g_object_class_override_property (object_class, + MM_GENERIC_GSM_PROP_POWER_UP_CMD, + MM_GENERIC_GSM_POWER_UP_CMD); + + g_object_class_override_property (object_class, + MM_GENERIC_GSM_PROP_POWER_DOWN_CMD, + MM_GENERIC_GSM_POWER_DOWN_CMD); +} + diff --git a/plugins/mm-modem-moto-c-gsm.h b/plugins/mm-modem-moto-c-gsm.h new file mode 100644 index 0000000..eb1dad1 --- /dev/null +++ b/plugins/mm-modem-moto-c-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_MOTO_C_GSM_H +#define MM_MODEM_MOTO_C_GSM_H + +#include "mm-generic-gsm.h" + +#define MM_TYPE_MODEM_MOTO_C_GSM (mm_modem_moto_c_gsm_get_type ()) +#define MM_MODEM_MOTO_C_GSM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_MODEM_MOTO_C_GSM, MMModemMotoCGsm)) +#define MM_MODEM_MOTO_C_GSM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_MODEM_MOTO_C_GSM, MMModemMotoCGsmClass)) +#define MM_IS_MODEM_MOTO_C_GSM(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_MODEM_MOTO_C_GSM)) +#define MM_IS_MODEM_MOTO_C_GSM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_MODEM_MOTO_C_GSM)) +#define MM_MODEM_MOTO_C_GSM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_MODEM_MOTO_C_GSM, MMModemMotoCGsmClass)) + +typedef struct { + MMGenericGsm parent; +} MMModemMotoCGsm; + +typedef struct { + MMGenericGsmClass parent; +} MMModemMotoCGsmClass; + +GType mm_modem_moto_c_gsm_get_type (void); + +MMModem *mm_modem_moto_c_gsm_new (const char *device, + const char *driver, + const char *plugin_name); + +#endif /* MM_MODEM_MOTO_C_GSM_H */ diff --git a/plugins/mm-modem-nokia.c b/plugins/mm-modem-nokia.c new file mode 100644 index 0000000..677a089 --- /dev/null +++ b/plugins/mm-modem-nokia.c @@ -0,0 +1,142 @@ +/* -*- 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. + */ + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> +#include "mm-modem-nokia.h" +#include "mm-serial-parsers.h" + +static void modem_init (MMModem *modem_class); + +G_DEFINE_TYPE_EXTENDED (MMModemNokia, mm_modem_nokia, MM_TYPE_GENERIC_GSM, 0, + G_IMPLEMENT_INTERFACE (MM_TYPE_MODEM, modem_init)) + + +MMModem * +mm_modem_nokia_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_NOKIA, + MM_MODEM_MASTER_DEVICE, device, + MM_MODEM_DRIVER, driver, + MM_MODEM_PLUGIN, plugin, + NULL)); +} + +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_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; + + 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); + } + + return !!port; +} + +/*****************************************************************************/ + +static void +modem_init (MMModem *modem_class) +{ + modem_class->grab_port = grab_port; +} + +static void +mm_modem_nokia_init (MMModemNokia *self) +{ +} + +static void +set_property (GObject *object, guint prop_id, + const GValue *value, GParamSpec *pspec) +{ + /* gobject does not like to just have get_property and seems to + * to not honour our overriden properties ... keep this as an empty + * func around */ +} + +static void +get_property (GObject *object, guint prop_id, + GValue *value, GParamSpec *pspec) +{ + /* Nokia headsets (at least N85) do not support "power on"; they do + * support "power off" but you proabably do not want to turn off the + * power on your telephone if something went wrong with connecting + * process. So, disabling both these operations. The Nokia GSM/UMTS command + * reference v1.2 also states that only CFUN=0 (turn off but still charge) + * and CFUN=1 (full functionality) are supported, and since the phone has + * to be in CFUN=1 before we'll be able to talk to it in the first place, + * we shouldn't bother with CFUN at all. + */ + switch (prop_id) { + case MM_GENERIC_GSM_PROP_POWER_UP_CMD: + g_value_set_string (value, ""); + break; + case MM_GENERIC_GSM_PROP_POWER_DOWN_CMD: + g_value_set_string (value, ""); + break; + default: + break; + } +} + +static void +mm_modem_nokia_class_init (MMModemNokiaClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + mm_modem_nokia_parent_class = g_type_class_peek_parent (klass); + + object_class->get_property = get_property; + object_class->set_property = set_property; + + g_object_class_override_property (object_class, + MM_GENERIC_GSM_PROP_POWER_UP_CMD, + MM_GENERIC_GSM_POWER_UP_CMD); + + g_object_class_override_property (object_class, + MM_GENERIC_GSM_PROP_POWER_DOWN_CMD, + MM_GENERIC_GSM_POWER_DOWN_CMD); +} + diff --git a/plugins/mm-modem-nokia.h b/plugins/mm-modem-nokia.h new file mode 100644 index 0000000..0e24619 --- /dev/null +++ b/plugins/mm-modem-nokia.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_NOKIA_H +#define MM_MODEM_NOKIA_H + +#include "mm-generic-gsm.h" + +#define MM_TYPE_MODEM_NOKIA (mm_modem_nokia_get_type ()) +#define MM_MODEM_NOKIA(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_MODEM_NOKIA, MMModemNokia)) +#define MM_MODEM_NOKIA_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_MODEM_NOKIA, MMModemNokiaClass)) +#define MM_IS_MODEM_NOKIA(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_MODEM_NOKIA)) +#define MM_IS_MODEM_NOKIA_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_MODEM_NOKIA)) +#define MM_MODEM_NOKIA_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_MODEM_NOKIA, MMModemNokiaClass)) + +typedef struct { + MMGenericGsm parent; +} MMModemNokia; + +typedef struct { + MMGenericGsmClass parent; +} MMModemNokiaClass; + +GType mm_modem_nokia_get_type (void); + +MMModem *mm_modem_nokia_new (const char *data, + const char *driver, + const char *plugin); + +#endif /* MM_MODEM_NOKIA_H */ diff --git a/plugins/mm-modem-novatel-gsm.c b/plugins/mm-modem-novatel-gsm.c new file mode 100644 index 0000000..8189627 --- /dev/null +++ b/plugins/mm-modem-novatel-gsm.c @@ -0,0 +1,184 @@ +/* -*- 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. + */ + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> +#include "mm-modem-novatel-gsm.h" +#include "mm-errors.h" +#include "mm-callback-info.h" + +static void modem_init (MMModem *modem_class); + +G_DEFINE_TYPE_EXTENDED (MMModemNovatelGsm, mm_modem_novatel_gsm, MM_TYPE_GENERIC_GSM, 0, + G_IMPLEMENT_INTERFACE (MM_TYPE_MODEM, modem_init)) + + +MMModem * +mm_modem_novatel_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_NOVATEL_GSM, + MM_MODEM_MASTER_DEVICE, device, + MM_MODEM_DRIVER, driver, + MM_MODEM_PLUGIN, plugin, + NULL)); +} + +/*****************************************************************************/ +/* Modem class override functions */ +/*****************************************************************************/ + +static void +init_modem_done (MMSerialPort *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); +} + +static void +pin_check_done (MMModem *modem, 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; + } + + /* 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); +} + +static void +pre_init_done (MMSerialPort *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); + 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); +} + +static void +enable_flash_done (MMSerialPort *port, GError *error, gpointer user_data) +{ + MMCallbackInfo *info = 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); +} + +static void +do_enable (MMGenericGsm *modem, MMModemFn callback, gpointer user_data) +{ + MMCallbackInfo *info; + MMSerialPort *primary; + + primary = mm_generic_gsm_get_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); +} + +static void +dmat_callback (MMSerialPort *port, + GString *response, + GError *error, + gpointer user_data) +{ + mm_serial_port_close (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_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; + + 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); + } + + return !!port; +} + +/*****************************************************************************/ + +static void +modem_init (MMModem *modem_class) +{ + modem_class->grab_port = grab_port; +} + +static void +mm_modem_novatel_gsm_init (MMModemNovatelGsm *self) +{ +} + +static void +mm_modem_novatel_gsm_class_init (MMModemNovatelGsmClass *klass) +{ + MMGenericGsmClass *gsm_class = MM_GENERIC_GSM_CLASS (klass); + + mm_modem_novatel_gsm_parent_class = g_type_class_peek_parent (klass); + + gsm_class->do_enable = do_enable; +} + diff --git a/plugins/mm-modem-novatel-gsm.h b/plugins/mm-modem-novatel-gsm.h new file mode 100644 index 0000000..c2e1138 --- /dev/null +++ b/plugins/mm-modem-novatel-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_NOVATEL_GSM_H +#define MM_MODEM_NOVATEL_GSM_H + +#include "mm-generic-gsm.h" + +#define MM_TYPE_MODEM_NOVATEL_GSM (mm_modem_novatel_gsm_get_type ()) +#define MM_MODEM_NOVATEL_GSM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_MODEM_NOVATEL_GSM, MMModemNovatelGsm)) +#define MM_MODEM_NOVATEL_GSM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_MODEM_NOVATEL_GSM, MMModemNovatelGsmClass)) +#define MM_IS_MODEM_NOVATEL_GSM(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_MODEM_NOVATEL_GSM)) +#define MM_IS_MODEM_NOVATEL_GSM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_MODEM_NOVATEL_GSM)) +#define MM_MODEM_NOVATEL_GSM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_MODEM_NOVATEL_GSM, MMModemNovatelGsmClass)) + +typedef struct { + MMGenericGsm parent; +} MMModemNovatelGsm; + +typedef struct { + MMGenericGsmClass parent; +} MMModemNovatelGsmClass; + +GType mm_modem_novatel_gsm_get_type (void); + +MMModem *mm_modem_novatel_gsm_new (const char *device, + const char *driver, + const char *plugin_name); + +#endif /* MM_MODEM_NOVATEL_GSM_H */ diff --git a/plugins/mm-modem-option.c b/plugins/mm-modem-option.c new file mode 100644 index 0000000..2076ae6 --- /dev/null +++ b/plugins/mm-modem-option.c @@ -0,0 +1,244 @@ +/* -*- 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. + */ + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> +#include "mm-modem-option.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); + +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)) + + +MMModem * +mm_modem_option_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_OPTION, + MM_MODEM_MASTER_DEVICE, device, + MM_MODEM_DRIVER, driver, + MM_MODEM_PLUGIN, plugin, + NULL)); +} + +/*****************************************************************************/ + +static void +pin_check_done (MMModem *modem, GError *error, gpointer user_data) +{ + MMCallbackInfo *info = (MMCallbackInfo *) user_data; + + mm_generic_gsm_enable_complete (MM_GENERIC_GSM (modem), error, info); +} + +static gboolean +option_enabled (gpointer data) +{ + MMCallbackInfo *info = (MMCallbackInfo *) data; + + /* 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); + return FALSE; +} + +static void +parent_enable_done (MMModem *modem, GError *error, gpointer user_data) +{ + MMCallbackInfo *info = (MMCallbackInfo *) user_data; + + if (error) { + mm_generic_gsm_enable_complete (MM_GENERIC_GSM (modem), error, info); + return; + } + + /* Option returns OK on +CFUN=1 right away but needs some time + * to finish initialization + */ + g_timeout_add_seconds (10, option_enabled, info); +} + +static void +enable (MMModem *modem, + MMModemFn callback, + gpointer user_data) +{ + MMModem *parent_modem_iface; + 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); +} + +static void +get_network_mode_done (MMSerialPort *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)) { + 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) +{ + 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_OPSYS?", 3, get_network_mode_done, info); +} + +static void +set_network_mode_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); +} + +static void +set_network_mode (MMModemGsmNetwork *modem, + MMModemGsmMode mode, + MMModemFn callback, + gpointer user_data) +{ + 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; + } + + 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); +} + +/*****************************************************************************/ + +static void +modem_init (MMModem *modem_class) +{ + modem_class->enable = enable; +} + +static void +modem_gsm_network_init (MMModemGsmNetwork *class) +{ + class->set_network_mode = set_network_mode; + class->get_network_mode = get_network_mode; +} + +static void +mm_modem_option_init (MMModemOption *self) +{ +} + +static void +mm_modem_option_class_init (MMModemOptionClass *klass) +{ + mm_modem_option_parent_class = g_type_class_peek_parent (klass); +} + diff --git a/plugins/mm-modem-option.h b/plugins/mm-modem-option.h new file mode 100644 index 0000000..4e88607 --- /dev/null +++ b/plugins/mm-modem-option.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_OPTION_H +#define MM_MODEM_OPTION_H + +#include "mm-generic-gsm.h" + +#define MM_TYPE_MODEM_OPTION (mm_modem_option_get_type ()) +#define MM_MODEM_OPTION(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_MODEM_OPTION, MMModemOption)) +#define MM_MODEM_OPTION_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_MODEM_OPTION, MMModemOptionClass)) +#define MM_IS_MODEM_OPTION(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_MODEM_OPTION)) +#define MM_IS_MODEM_OPTION_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_MODEM_OPTION)) +#define MM_MODEM_OPTION_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_MODEM_OPTION, MMModemOptionClass)) + +typedef struct { + MMGenericGsm parent; +} MMModemOption; + +typedef struct { + MMGenericGsmClass parent; +} MMModemOptionClass; + +GType mm_modem_option_get_type (void); + +MMModem *mm_modem_option_new (const char *device, + const char *driver, + const char *plugin_name); + +#endif /* MM_MODEM_OPTION_H */ diff --git a/plugins/mm-modem-sierra-cdma.c b/plugins/mm-modem-sierra-cdma.c new file mode 100644 index 0000000..4f3140b --- /dev/null +++ b/plugins/mm-modem-sierra-cdma.c @@ -0,0 +1,383 @@ +/* -*- 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. + */ + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <errno.h> +#include <ctype.h> + +#define G_UDEV_API_IS_SUBJECT_TO_CHANGE +#include <gudev/gudev.h> + +#include "mm-modem-sierra-cdma.h" +#include "mm-errors.h" +#include "mm-callback-info.h" +#include "mm-serial-port.h" +#include "mm-serial-parsers.h" + +G_DEFINE_TYPE (MMModemSierraCdma, mm_modem_sierra_cdma, MM_TYPE_GENERIC_CDMA) + +#define MM_MODEM_SIERRA_CDMA_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), MM_TYPE_MODEM_SIERRA_CDMA, MMModemSierraCdmaPrivate)) + +typedef enum { + SYS_MODE_UNKNOWN, + SYS_MODE_NO_SERVICE, + SYS_MODE_CDMA_1X, + SYS_MODE_EVDO_REV0, + SYS_MODE_EVDO_REVA +} SysMode; + +typedef struct { + SysMode sys_mode; +} MMModemSierraCdmaPrivate; + +MMModem * +mm_modem_sierra_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_SIERRA_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)); +} + +/*****************************************************************************/ + +#define MODEM_REG_TAG "Modem has registered" +#define GENERIC_ROAM_TAG "Roaming:" +#define ROAM_1X_TAG "1xRoam:" +#define ROAM_EVDO_TAG "HDRRoam:" +#define SYS_MODE_TAG "Sys Mode:" +#define SYS_MODE_NO_SERVICE_TAG "NO SRV" +#define SYS_MODE_EVDO_TAG "HDR" +#define SYS_MODE_1X_TAG "1x" +#define EVDO_REV_TAG "HDR Revision:" +#define SID_TAG "SID:" + +static gboolean +get_roam_value (const char *reply, const char *tag, gboolean *roaming) +{ + char *p; + + p = strstr (reply, tag); + if (!p) + return FALSE; + + p += strlen (tag); + while (*p && isspace (*p)) + p++; + if (*p == '1') { + *roaming = TRUE; + return TRUE; + } else if (*p == '0') { + *roaming = FALSE; + return TRUE; + } + + return FALSE; +} + +static gboolean +sys_mode_has_service (SysMode mode) +{ + return ( mode == SYS_MODE_CDMA_1X + || mode == SYS_MODE_EVDO_REV0 + || mode == SYS_MODE_EVDO_REVA); +} + +static void +status_done (MMSerialPort *port, + GString *response, + GError *error, + gpointer user_data) +{ + MMCallbackInfo *info = (MMCallbackInfo *) user_data; + MMModemSierraCdmaPrivate *priv = MM_MODEM_SIERRA_CDMA_GET_PRIVATE (info->modem); + char **lines, **iter; + gboolean registered = FALSE; + gboolean have_sid = FALSE; + SysMode evdo_mode = SYS_MODE_UNKNOWN; + SysMode sys_mode = SYS_MODE_UNKNOWN; + gboolean cdma_1x_set = FALSE, evdo_set = FALSE; + + if (error) { + info->error = g_error_copy (error); + goto done; + } + + lines = g_strsplit_set (response->str, "\n\r", 0); + if (!lines) { + /* Whatever, just use default registration state */ + goto done; + } + + /* Sierra CDMA parts have two general formats depending on whether they + * support EVDO or not. EVDO parts report both 1x and EVDO roaming status + * while of course 1x parts only report 1x status. Some modems also do not + * report the Roaming information (MP 555 GPS). + * + * AT!STATUS responses: + * + * Unregistered MC5725: + * ----------------------- + * Current band: PCS CDMA + * Current channel: 350 + * SID: 0 NID: 0 1xRoam: 0 HDRRoam: 0 + * Temp: 33 State: 100 Sys Mode: NO SRV + * Pilot NOT acquired + * Modem has NOT registered + * + * Registered MC5725: + * ----------------------- + * Current band: Cellular Sleep + * Current channel: 775 + * SID: 30 NID: 2 1xRoam: 0 HDRRoam: 0 + * Temp: 29 State: 200 Sys Mode: HDR + * Pilot acquired + * Modem has registered + * HDR Revision: A + * + * Unregistered AC580: + * ----------------------- + * Current band: PCS CDMA + * Current channel: 350 + * SID: 0 NID: 0 Roaming: 0 + * Temp: 39 State: 100 Scan Mode: 0 + * Pilot NOT acquired + * Modem has NOT registered + * + * Registered AC580: + * ----------------------- + * Current band: Cellular Sleep + * Current channel: 548 + * SID: 26 NID: 1 Roaming: 1 + * Temp: 39 State: 200 Scan Mode: 0 + * Pilot Acquired + * Modem has registered + */ + + /* We have to handle the two formats slightly differently; for newer formats + * with "Sys Mode", we consider the modem registered if the Sys Mode is not + * "NO SRV". The explicit registration status is just icing on the cake. + * For older formats (no "Sys Mode") we treat the modem as registered if + * the SID is non-zero. + */ + + for (iter = lines; iter && *iter; iter++) { + gboolean bool_val = FALSE; + char *p; + + if (!strncmp (*iter, MODEM_REG_TAG, strlen (MODEM_REG_TAG))) { + registered = TRUE; + continue; + } + + /* 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; + } + + /* Current system mode */ + p = strstr (*iter, SYS_MODE_TAG); + if (p) { + p += strlen (SYS_MODE_TAG); + while (*p && isspace (*p)) + p++; + if (!strncmp (p, SYS_MODE_NO_SERVICE_TAG, strlen (SYS_MODE_NO_SERVICE_TAG))) + 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))) + sys_mode = SYS_MODE_CDMA_1X; + } + + /* Current EVDO revision if system mode is EVDO */ + p = strstr (*iter, EVDO_REV_TAG); + if (p) { + p += strlen (EVDO_REV_TAG); + while (*p && isspace (*p)) + p++; + if (*p == 'A') + evdo_mode = SYS_MODE_EVDO_REVA; + else if (*p == '0') + evdo_mode = SYS_MODE_EVDO_REV0; + } + + /* SID */ + p = strstr (*iter, SID_TAG); + if (p) { + p += strlen (SID_TAG); + while (*p && isspace (*p)) + p++; + if (isdigit (*p) && (*p != '0')) + have_sid = TRUE; + } + } + + /* Update current system mode */ + if (sys_mode == SYS_MODE_EVDO_REV0 || sys_mode == SYS_MODE_EVDO_REVA) { + /* 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); + } else { + /* Not registered */ + mm_generic_cdma_query_reg_state_set_callback_1x_state (info, MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN); + mm_generic_cdma_query_reg_state_set_callback_evdo_state (info, MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN); + } + +done: + mm_callback_info_schedule (info); +} + +static void +query_registration_state (MMGenericCdma *cdma, + 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); + + 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; + } + + /* Use secondary port if primary is connected */ + port = secondary; + } + + mm_serial_port_queue_command (port, "!STATUS", 3, status_done, info); +} + +static void +pcstate_done (MMSerialPort *port, + GString *response, + GError *error, + gpointer user_data) +{ + /* Ignore errors for now; we're not sure if all Sierra CDMA devices support + * at!pcstate. + */ + mm_callback_info_schedule ((MMCallbackInfo *) user_data); +} + +static void +post_enable (MMGenericCdma *cdma, + MMModemFn callback, + gpointer user_data) +{ + MMCallbackInfo *info; + MMSerialPort *primary; + + info = mm_callback_info_new (MM_MODEM (cdma), callback, user_data); + + primary = mm_generic_cdma_get_port (cdma, MM_PORT_TYPE_PRIMARY); + g_assert (primary); + + mm_serial_port_queue_command (primary, "!pcstate=1", 5, pcstate_done, info); +} + +static void +post_disable (MMGenericCdma *cdma, + MMModemFn callback, + gpointer user_data) +{ + MMCallbackInfo *info; + MMSerialPort *primary; + + info = mm_callback_info_new (MM_MODEM (cdma), callback, user_data); + + primary = mm_generic_cdma_get_port (cdma, MM_PORT_TYPE_PRIMARY); + g_assert (primary); + + mm_serial_port_queue_command (primary, "!pcstate=0", 5, pcstate_done, info); +} + +/*****************************************************************************/ + +static void +mm_modem_sierra_cdma_init (MMModemSierraCdma *self) +{ +} + +static void +mm_modem_sierra_cdma_class_init (MMModemSierraCdmaClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + MMGenericCdmaClass *cdma_class = MM_GENERIC_CDMA_CLASS (klass); + + mm_modem_sierra_cdma_parent_class = g_type_class_peek_parent (klass); + g_type_class_add_private (object_class, sizeof (MMModemSierraCdmaPrivate)); + + cdma_class->query_registration_state = query_registration_state; + cdma_class->post_enable = post_enable; + cdma_class->post_disable = post_disable; +} + diff --git a/plugins/mm-modem-sierra-cdma.h b/plugins/mm-modem-sierra-cdma.h new file mode 100644 index 0000000..9111b73 --- /dev/null +++ b/plugins/mm-modem-sierra-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 Red Hat, Inc. + */ + +#ifndef MM_MODEM_SIERRA_CDMA_H +#define MM_MODEM_SIERRA_CDMA_H + +#include "mm-generic-cdma.h" + +#define MM_TYPE_MODEM_SIERRA_CDMA (mm_modem_sierra_cdma_get_type ()) +#define MM_MODEM_SIERRA_CDMA(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_MODEM_SIERRA_CDMA, MMModemSierraCdma)) +#define MM_MODEM_SIERRA_CDMA_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_MODEM_SIERRA_CDMA, MMModemSierraCdmaClass)) +#define MM_IS_MODEM_SIERRA_CDMA(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_MODEM_SIERRA_CDMA)) +#define MM_IS_MODEM_SIERRA_CDMA_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_MODEM_SIERRA_CDMA)) +#define MM_MODEM_SIERRA_CDMA_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_MODEM_SIERRA_CDMA, MMModemSierraCdmaClass)) + +typedef struct { + MMGenericCdma parent; +} MMModemSierraCdma; + +typedef struct { + MMGenericCdmaClass parent; +} MMModemSierraCdmaClass; + +GType mm_modem_sierra_cdma_get_type (void); + +MMModem *mm_modem_sierra_cdma_new (const char *device, + const char *driver, + const char *plugin, + gboolean evdo_rev0, + gboolean evdo_revA); + +#endif /* MM_MODEM_SIERRA_CDMA_H */ diff --git a/plugins/mm-modem-sierra-gsm.c b/plugins/mm-modem-sierra-gsm.c new file mode 100644 index 0000000..ee82234 --- /dev/null +++ b/plugins/mm-modem-sierra-gsm.c @@ -0,0 +1,145 @@ +/* -*- 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. + */ + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> +#include "mm-modem-sierra-gsm.h" +#include "mm-errors.h" +#include "mm-callback-info.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)) + + +MMModem * +mm_modem_sierra_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_SIERRA_GSM, + MM_MODEM_MASTER_DEVICE, device, + MM_MODEM_DRIVER, driver, + MM_MODEM_PLUGIN, plugin, + NULL)); +} + +/*****************************************************************************/ +/* Modem class override functions */ +/*****************************************************************************/ + +static void +pin_check_done (MMModem *modem, GError *error, gpointer user_data) +{ + MMCallbackInfo *info = (MMCallbackInfo *) user_data; + + mm_generic_gsm_enable_complete (MM_GENERIC_GSM (modem), error, info); +} + +static gboolean +sierra_enabled (gpointer data) +{ + MMCallbackInfo *info = (MMCallbackInfo *) data; + + /* 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; +} + +static void +parent_enable_done (MMModem *modem, GError *error, gpointer user_data) +{ + MMCallbackInfo *info = (MMCallbackInfo *) user_data; + + if (error) { + mm_generic_gsm_enable_complete (MM_GENERIC_GSM (modem), error, 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); +} + +static void +enable (MMModem *modem, MMModemFn callback, gpointer user_data) +{ + MMModem *parent_modem_iface; + 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); +} + +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_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; + + 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); + + return !!port; +} + +/*****************************************************************************/ + +static void +modem_init (MMModem *modem_class) +{ + modem_class->enable = enable; + modem_class->grab_port = grab_port; +} + +static void +mm_modem_sierra_gsm_init (MMModemSierraGsm *self) +{ +} + +static void +mm_modem_sierra_gsm_class_init (MMModemSierraGsmClass *klass) +{ + mm_modem_sierra_gsm_parent_class = g_type_class_peek_parent (klass); +} + diff --git a/plugins/mm-modem-sierra-gsm.h b/plugins/mm-modem-sierra-gsm.h new file mode 100644 index 0000000..dd84b30 --- /dev/null +++ b/plugins/mm-modem-sierra-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_SIERRA_GSM_H +#define MM_MODEM_SIERRA_GSM_H + +#include "mm-generic-gsm.h" + +#define MM_TYPE_MODEM_SIERRA_GSM (mm_modem_sierra_gsm_get_type ()) +#define MM_MODEM_SIERRA_GSM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_MODEM_SIERRA_GSM, MMModemSierraGsm)) +#define MM_MODEM_SIERRA_GSM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_MODEM_SIERRA_GSM, MMModemSierraGsmClass)) +#define MM_IS_MODEM_SIERRA_GSM(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_MODEM_SIERRA_GSM)) +#define MM_IS_MODEM_SIERRA_GSM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_MODEM_SIERRA_GSM)) +#define MM_MODEM_SIERRA_GSM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_MODEM_SIERRA_GSM, MMModemSierraGsmClass)) + +typedef struct { + MMGenericGsm parent; +} MMModemSierraGsm; + +typedef struct { + MMGenericGsmClass parent; +} MMModemSierraGsmClass; + +GType mm_modem_sierra_gsm_get_type (void); + +MMModem *mm_modem_sierra_gsm_new (const char *device, + const char *driver, + const char *plugin_name); + +#endif /* MM_MODEM_SIERRA_GSM_H */ diff --git a/plugins/mm-modem-zte.c b/plugins/mm-modem-zte.c new file mode 100644 index 0000000..92c23ae --- /dev/null +++ b/plugins/mm-modem-zte.c @@ -0,0 +1,235 @@ +/* -*- 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. + */ + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> +#include "mm-modem-zte.h" +#include "mm-serial-port.h" +#include "mm-errors.h" +#include "mm-callback-info.h" + +static void modem_init (MMModem *modem_class); + +G_DEFINE_TYPE_EXTENDED (MMModemZte, mm_modem_zte, MM_TYPE_GENERIC_GSM, 0, + G_IMPLEMENT_INTERFACE (MM_TYPE_MODEM, modem_init)) + +#define MM_MODEM_ZTE_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), MM_TYPE_MODEM_ZTE, MMModemZtePrivate)) + +typedef struct { + gboolean init_retried; +} MMModemZtePrivate; + +MMModem * +mm_modem_zte_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_ZTE, + MM_MODEM_MASTER_DEVICE, device, + MM_MODEM_DRIVER, driver, + MM_MODEM_PLUGIN, plugin, + NULL)); +} + +/*****************************************************************************/ +/* Modem class override functions */ +/*****************************************************************************/ + +static void +init_modem_done (MMSerialPort *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); +} + +static void +pin_check_done (MMModem *modem, 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); + 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); +} + +static void enable_flash_done (MMSerialPort *port, + GError *error, + gpointer user_data); + +static void +pre_init_done (MMSerialPort *port, + GString *response, + GError *error, + gpointer user_data) +{ + MMCallbackInfo *info = (MMCallbackInfo *) user_data; + MMModemZtePrivate *priv = MM_MODEM_ZTE_GET_PRIVATE (info->modem); + + 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)) { + priv->init_retried = TRUE; + enable_flash_done (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); + } +} + +static void +enable_flash_done (MMSerialPort *port, GError *error, gpointer user_data) +{ + MMCallbackInfo *info = (MMCallbackInfo *) 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); +} + +static void +do_enable (MMGenericGsm *modem, MMModemFn callback, gpointer user_data) +{ + MMModemZtePrivate *priv = MM_MODEM_ZTE_GET_PRIVATE (modem); + MMCallbackInfo *info; + MMSerialPort *primary; + + priv->init_retried = FALSE; + + primary = mm_generic_gsm_get_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); +} + +static void +disable (MMModem *modem, + MMModemFn callback, + gpointer user_data) +{ + MMModemZtePrivate *priv = MM_MODEM_ZTE_GET_PRIVATE (modem); + MMModem *parent_modem_iface; + + priv->init_retried = FALSE; + + /* Do the normal disable stuff */ + parent_modem_iface = g_type_interface_peek_parent (MM_MODEM_GET_INTERFACE (modem)); + parent_modem_iface->disable (modem, callback, user_data); +} + +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_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; + + port = mm_generic_gsm_grab_port (gsm, subsys, name, ptype, error); + if (port && MM_IS_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); + 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); + 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); + 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); + 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); + 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_zte_init (MMModemZte *self) +{ +} + +static void +mm_modem_zte_class_init (MMModemZteClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + MMGenericGsmClass *gsm_class = MM_GENERIC_GSM_CLASS (klass); + + mm_modem_zte_parent_class = g_type_class_peek_parent (klass); + g_type_class_add_private (object_class, sizeof (MMModemZtePrivate)); + + gsm_class->do_enable = do_enable; +} + diff --git a/plugins/mm-modem-zte.h b/plugins/mm-modem-zte.h new file mode 100644 index 0000000..112bae0 --- /dev/null +++ b/plugins/mm-modem-zte.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_ZTE_H +#define MM_MODEM_ZTE_H + +#include "mm-generic-gsm.h" + +#define MM_TYPE_MODEM_ZTE (mm_modem_zte_get_type ()) +#define MM_MODEM_ZTE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_MODEM_ZTE, MMModemZte)) +#define MM_MODEM_ZTE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_MODEM_ZTE, MMModemZteClass)) +#define MM_IS_MODEM_ZTE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_MODEM_ZTE)) +#define MM_IS_MODEM_ZTE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_MODEM_ZTE)) +#define MM_MODEM_ZTE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_MODEM_ZTE, MMModemZteClass)) + +typedef struct { + MMGenericGsm parent; +} MMModemZte; + +typedef struct { + MMGenericGsmClass parent; +} MMModemZteClass; + +GType mm_modem_zte_get_type (void); + +MMModem *mm_modem_zte_new (const char *device, + const char *driver, + const char *plugin); + +#endif /* MM_MODEM_ZTE_H */ diff --git a/plugins/mm-plugin-anydata.c b/plugins/mm-plugin-anydata.c new file mode 100644 index 0000000..e451714 --- /dev/null +++ b/plugins/mm-plugin-anydata.c @@ -0,0 +1,179 @@ +/* -*- 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-anydata.h" +#include "mm-modem-anydata-cdma.h" + +G_DEFINE_TYPE (MMPluginAnydata, mm_plugin_anydata, 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_ANYDATA, + MM_PLUGIN_BASE_NAME, "AnyData", + 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) +{ + /* Only CDMA for now */ + if (capabilities & CAP_CDMA) + 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; + const char *subsys, *name; + guint16 vendor = 0; + + /* 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; + + if (vendor != 0x16d5) + 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, *physdev = NULL; + MMModem *modem = NULL; + const char *name, *subsys, *devfile, *sysfs_path; + guint32 caps; + + port = mm_plugin_base_supports_task_get_port (task); + g_assert (port); + + devfile = g_udev_device_get_device_file (port); + if (!devfile) { + g_set_error (error, 0, 0, "Could not get port's sysfs file."); + 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); + if (caps & MM_PLUGIN_BASE_PORT_CAP_GSM) { + g_set_error (error, 0, 0, "Only CDMA modems are currently supported by this plugin."); + return NULL; + } + + if (!existing) { + if (caps & CAP_CDMA) { + modem = mm_modem_anydata_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, MM_PORT_TYPE_UNKNOWN, NULL, error)) { + g_object_unref (modem); + 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; + } + } + + return modem; +} + +/*****************************************************************************/ + +static void +mm_plugin_anydata_init (MMPluginAnydata *self) +{ + g_signal_connect (self, "probe-result", G_CALLBACK (probe_result), NULL); +} + +static void +mm_plugin_anydata_class_init (MMPluginAnydataClass *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-anydata.h b/plugins/mm-plugin-anydata.h new file mode 100644 index 0000000..b71aad3 --- /dev/null +++ b/plugins/mm-plugin-anydata.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_ANYDATA_H +#define MM_PLUGIN_ANYDATA_H + +#include "mm-plugin-base.h" + +#define MM_TYPE_PLUGIN_ANYDATA (mm_plugin_anydata_get_type ()) +#define MM_PLUGIN_ANYDATA(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_PLUGIN_ANYDATA, MMPluginAnydata)) +#define MM_PLUGIN_ANYDATA_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_PLUGIN_ANYDATA, MMPluginAnydataClass)) +#define MM_IS_PLUGIN_ANYDATA(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_PLUGIN_ANYDATA)) +#define MM_IS_PLUGIN_ANYDATA_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_PLUGIN_ANYDATA)) +#define MM_PLUGIN_ANYDATA_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_PLUGIN_ANYDATA, MMPluginAnydataClass)) + +typedef struct { + MMPluginBase parent; +} MMPluginAnydata; + +typedef struct { + MMPluginBaseClass parent; +} MMPluginAnydataClass; + +GType mm_plugin_anydata_get_type (void); + +G_MODULE_EXPORT MMPlugin *mm_plugin_create (void); + +#endif /* MM_PLUGIN_ANYDATA_H */ diff --git a/plugins/mm-plugin-generic.c b/plugins/mm-plugin-generic.c new file mode 100644 index 0000000..e5e2ade --- /dev/null +++ b/plugins/mm-plugin-generic.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 Dan Williams <dcbw@redhat.com> + */ + +#include <string.h> +#include <termios.h> +#include <unistd.h> +#include <fcntl.h> +#include <errno.h> +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <getopt.h> +#include <time.h> + +#include <gmodule.h> + +#include "mm-plugin-generic.h" +#include "mm-generic-gsm.h" +#include "mm-generic-cdma.h" +#include "mm-errors.h" +#include "mm-serial-parsers.h" + +G_DEFINE_TYPE (MMPluginGeneric, mm_plugin_generic, 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_GENERIC, + MM_PLUGIN_BASE_NAME, MM_PLUGIN_GENERIC_NAME, + 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 5; + if (capabilities & CAP_CDMA) + return 5; + 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; + + /* 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; + + 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, *physdev = NULL; + MMModem *modem = NULL; + const char *name, *subsys, *devfile, *sysfs_path, *driver; + guint32 caps; + + port = mm_plugin_base_supports_task_get_port (task); + g_assert (port); + + subsys = g_udev_device_get_subsystem (port); + name = g_udev_device_get_name (port); + + devfile = g_udev_device_get_device_file (port); + if (!devfile) { + driver = mm_plugin_base_supports_task_get_driver (task); + if (!driver || strcmp (driver, "bluetooth")) { + g_set_error (error, 0, 0, "Could not get port's sysfs file."); + return NULL; + } else { + g_message ("%s: (%s/%s) WARNING: missing udev 'device' file", + mm_plugin_get_name (MM_PLUGIN (base)), + subsys, + name); + } + } + + 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); + 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))); + } 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, MM_PORT_TYPE_UNKNOWN, NULL, error)) { + g_object_unref (modem); + 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; + } + } + + return modem; +} + +/*****************************************************************************/ + +static void +mm_plugin_generic_init (MMPluginGeneric *self) +{ + g_signal_connect (self, "probe-result", G_CALLBACK (probe_result), NULL); +} + +static void +mm_plugin_generic_class_init (MMPluginGenericClass *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-generic.h b/plugins/mm-plugin-generic.h new file mode 100644 index 0000000..8d05689 --- /dev/null +++ b/plugins/mm-plugin-generic.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) 2009 Red Hat, Inc. + */ + +#ifndef MM_PLUGIN_GENERIC_H +#define MM_PLUGIN_GENERIC_H + +#include "mm-plugin.h" +#include "mm-plugin-base.h" + +#define MM_TYPE_PLUGIN_GENERIC (mm_plugin_generic_get_type ()) +#define MM_PLUGIN_GENERIC(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_PLUGIN_GENERIC, MMPluginGeneric)) +#define MM_PLUGIN_GENERIC_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_PLUGIN_GENERIC, MMPluginGenericClass)) +#define MM_IS_PLUGIN_GENERIC(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_PLUGIN_GENERIC)) +#define MM_IS_PLUGIN_GENERIC_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_PLUGIN_GENERIC)) +#define MM_PLUGIN_GENERIC_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_PLUGIN_GENERIC, MMPluginGenericClass)) + +typedef struct { + MMPluginBase parent; +} MMPluginGeneric; + +typedef struct { + MMPluginBaseClass parent; +} MMPluginGenericClass; + +GType mm_plugin_generic_get_type (void); + +G_MODULE_EXPORT MMPlugin *mm_plugin_create (void); + +#endif /* MM_PLUGIN_GENERIC_H */ diff --git a/plugins/mm-plugin-gobi.c b/plugins/mm-plugin-gobi.c new file mode 100644 index 0000000..77da965 --- /dev/null +++ b/plugins/mm-plugin-gobi.c @@ -0,0 +1,171 @@ +/* -*- 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. + */ + +#include <string.h> +#include <gmodule.h> +#define G_UDEV_API_IS_SUBJECT_TO_CHANGE +#include <gudev/gudev.h> + +#include "mm-plugin-gobi.h" +#include "mm-modem-gobi-gsm.h" +#include "mm-generic-cdma.h" + +G_DEFINE_TYPE (MMPluginGobi, mm_plugin_gobi, 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_GOBI, + MM_PLUGIN_BASE_NAME, "Gobi", + 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; + 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; + const char *driver; + + /* 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; + + driver = mm_plugin_base_supports_task_get_driver (task); + if (!driver || strcmp (driver, "qcserial")) + 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, *physdev = NULL; + MMModem *modem = NULL; + const char *name, *subsys, *sysfs_path; + guint32 caps; + + 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); + if (!existing) { + if (caps & MM_PLUGIN_BASE_PORT_CAP_GSM) { + modem = mm_modem_gobi_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, MM_PORT_TYPE_UNKNOWN, NULL, error)) { + g_object_unref (modem); + 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; + } + } + + return modem; +} + +/*****************************************************************************/ + +static void +mm_plugin_gobi_init (MMPluginGobi *self) +{ + g_signal_connect (self, "probe-result", G_CALLBACK (probe_result), NULL); +} + +static void +mm_plugin_gobi_class_init (MMPluginGobiClass *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-gobi.h b/plugins/mm-plugin-gobi.h new file mode 100644 index 0000000..aceba6b --- /dev/null +++ b/plugins/mm-plugin-gobi.h @@ -0,0 +1,44 @@ +/* -*- 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_PLUGIN_GOBI_H +#define MM_PLUGIN_GOBI_H + +#include "mm-plugin.h" +#include "mm-plugin-base.h" +#include "mm-generic-gsm.h" + +#define MM_TYPE_PLUGIN_GOBI (mm_plugin_gobi_get_type ()) +#define MM_PLUGIN_GOBI(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_PLUGIN_GOBI, MMPluginGobi)) +#define MM_PLUGIN_GOBI_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_PLUGIN_GOBI, MMPluginGobiClass)) +#define MM_IS_PLUGIN_GOBI(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_PLUGIN_GOBI)) +#define MM_IS_PLUGIN_GOBI_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_PLUGIN_GOBI)) +#define MM_PLUGIN_GOBI_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_PLUGIN_GOBI, MMPluginGobiClass)) + +typedef struct { + MMPluginBase parent; +} MMPluginGobi; + +typedef struct { + MMPluginBaseClass parent; +} MMPluginGobiClass; + +GType mm_plugin_gobi_get_type (void); + +G_MODULE_EXPORT MMPlugin *mm_plugin_create (void); + +#endif /* MM_PLUGIN_GOBI_H */ + diff --git a/plugins/mm-plugin-hso.c b/plugins/mm-plugin-hso.c new file mode 100644 index 0000000..8493c9c --- /dev/null +++ b/plugins/mm-plugin-hso.c @@ -0,0 +1,182 @@ +/* -*- 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. + */ + +#include <string.h> +#include <gmodule.h> + +#define G_UDEV_API_IS_SUBJECT_TO_CHANGE +#include <gudev/gudev.h> + +#include "mm-plugin-hso.h" +#include "mm-modem-hso.h" + +G_DEFINE_TYPE (MMPluginHso, mm_plugin_hso, 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_HSO, + MM_PLUGIN_BASE_NAME, "Option High-Speed", + NULL)); +} + +/*****************************************************************************/ + +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) +{ + 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; + const char *driver, *subsys; + + port = mm_plugin_base_supports_task_get_port (task); + + driver = mm_plugin_base_supports_task_get_driver (task); + if (!driver || strcmp (driver, "hso")) + return MM_PLUGIN_SUPPORTS_PORT_UNSUPPORTED; + + subsys = g_udev_device_get_subsystem (port); + g_assert (subsys); + if (!strcmp (subsys, "net")) { + mm_plugin_base_supports_task_complete (task, 10); + return MM_PLUGIN_SUPPORTS_PORT_IN_PROGRESS; + } + + 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, *physdev = NULL; + MMModem *modem = NULL; + const char *name, *subsys, *sysfs_path; + char *devfile; + guint32 caps; + + port = mm_plugin_base_supports_task_get_port (task); + g_assert (port); + + subsys = g_udev_device_get_subsystem (port); + name = g_udev_device_get_name (port); + + devfile = g_strdup (g_udev_device_get_device_file (port)); + if (!devfile) { + if (!strcmp (subsys, "net")) { + /* Apparently 'hso' doesn't set up the right links for the netdevice, + * and thus libgudev can't get the sysfs file path for it. + */ + devfile = g_strdup_printf ("/sys/class/net/%s", name); + if (!g_file_test (devfile, G_FILE_TEST_EXISTS)) { + g_free (devfile); + devfile = NULL; + } + } + + if (!devfile) { + g_set_error (error, 0, 0, "Could not get port's sysfs file."); + goto out; + } + } + + 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; + + if (!existing) { + modem = mm_modem_hso_new (sysfs_path, + mm_plugin_base_supports_task_get_driver (task), + mm_plugin_get_name (MM_PLUGIN (base))); + if (modem) { + if (!mm_modem_grab_port (modem, subsys, name, MM_PORT_TYPE_UNKNOWN, NULL, error)) { + g_object_unref (modem); + return NULL; + } + } + } else { + modem = existing; + if (!mm_modem_grab_port (modem, subsys, name, MM_PORT_TYPE_UNKNOWN, NULL, error)) + return NULL; + } + +out: + g_free (devfile); + return modem; +} + +/*****************************************************************************/ + +static void +mm_plugin_hso_init (MMPluginHso *self) +{ + g_signal_connect (self, "probe-result", G_CALLBACK (probe_result), NULL); +} + +static void +mm_plugin_hso_class_init (MMPluginHsoClass *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-hso.h b/plugins/mm-plugin-hso.h new file mode 100644 index 0000000..a3f4caf --- /dev/null +++ b/plugins/mm-plugin-hso.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_PLUGIN_HSO_H +#define MM_PLUGIN_HSO_H + +#include "mm-plugin.h" +#include "mm-plugin-base.h" +#include "mm-generic-gsm.h" + +#define MM_TYPE_PLUGIN_HSO (mm_plugin_hso_get_type ()) +#define MM_PLUGIN_HSO(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_PLUGIN_HSO, MMPluginHso)) +#define MM_PLUGIN_HSO_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_PLUGIN_HSO, MMPluginHsoClass)) +#define MM_IS_PLUGIN_HSO(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_PLUGIN_HSO)) +#define MM_IS_PLUGIN_HSO_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_PLUGIN_HSO)) +#define MM_PLUGIN_HSO_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_PLUGIN_HSO, MMPluginHsoClass)) + +typedef struct { + MMPluginBase parent; +} MMPluginHso; + +typedef struct { + MMPluginBaseClass parent; +} MMPluginHsoClass; + +GType mm_plugin_hso_get_type (void); + +G_MODULE_EXPORT MMPlugin *mm_plugin_create (void); + +#endif /* MM_PLUGIN_HSO_H */ diff --git a/plugins/mm-plugin-huawei.c b/plugins/mm-plugin-huawei.c new file mode 100644 index 0000000..ad799f0 --- /dev/null +++ b/plugins/mm-plugin-huawei.c @@ -0,0 +1,341 @@ +/* -*- 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. + */ + +#include <string.h> +#include <gmodule.h> + +#define G_UDEV_API_IS_SUBJECT_TO_CHANGE +#include <gudev/gudev.h> + +#include "mm-plugin-huawei.h" +#include "mm-generic-gsm.h" +#include "mm-generic-cdma.h" +#include "mm-modem-huawei-gsm.h" +#include "mm-modem-huawei-cdma.h" +#include "mm-serial-parsers.h" + +G_DEFINE_TYPE (MMPluginHuawei, mm_plugin_huawei, 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_HUAWEI, + MM_PLUGIN_BASE_NAME, "Huawei", + 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; + 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)); +} + +#define TAG_SUPPORTS_INFO "huawei-supports-info" + +typedef struct { + MMSerialPort *serial; + guint id; + gboolean secondary; +} HuaweiSupportsInfo; + +static void +huawei_supports_info_destroy (gpointer user_data) +{ + HuaweiSupportsInfo *info = user_data; + + if (info->id) + g_source_remove (info->id); + if (info->serial) + g_object_unref (info->serial); + memset (info, 0, sizeof (HuaweiSupportsInfo)); + g_free (info); +} + +static gboolean +probe_secondary_supported (gpointer user_data) +{ + MMPluginBaseSupportsTask *task = user_data; + HuaweiSupportsInfo *info; + + info = g_object_get_data (G_OBJECT (task), TAG_SUPPORTS_INFO); + + info->id = 0; + g_object_unref (info->serial); + info->serial = NULL; + + /* Yay, supported, we got an unsolicited message */ + info->secondary = TRUE; + mm_plugin_base_supports_task_complete (task, 10); + return FALSE; +} + +static void +probe_secondary_handle_msg (MMSerialPort *port, + GMatchInfo *match_info, + gpointer user_data) +{ + MMPluginBaseSupportsTask *task = user_data; + HuaweiSupportsInfo *info; + + info = g_object_get_data (G_OBJECT (task), TAG_SUPPORTS_INFO); + g_source_remove (info->id); + info->id = g_idle_add (probe_secondary_supported, task); +} + +static gboolean +probe_secondary_timeout (gpointer user_data) +{ + MMPluginBaseSupportsTask *task = user_data; + HuaweiSupportsInfo *info; + + 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); + return FALSE; +} + +static void +add_regex (MMSerialPort *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); + g_regex_unref (regex); +} + +static MMPluginSupportsResult +supports_port (MMPluginBase *base, + MMModem *existing, + MMPluginBaseSupportsTask *task) +{ + GUdevDevice *port; + guint32 cached = 0, level; + const char *subsys, *name; + int usbif; + guint16 vendor = 0, product = 0; + guint32 existing_type = MM_MODEM_TYPE_UNKNOWN; + + /* 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, &product)) + return MM_PLUGIN_SUPPORTS_PORT_UNSUPPORTED; + + if (vendor != 0x12d1) + return MM_PLUGIN_SUPPORTS_PORT_UNSUPPORTED; + + usbif = g_udev_device_get_property_as_int (port, "ID_USB_INTERFACE_NUM"); + if (usbif < 0) + return MM_PLUGIN_SUPPORTS_PORT_UNSUPPORTED; + + /* The secondary ports don't necessarily respond correctly to probing, so + * we need to use the first port that does respond to probing to create the + * right type of mode (GSM or CDMA), and then re-check the other interfaces. + */ + if (!existing && usbif != 0) + return MM_PLUGIN_SUPPORTS_PORT_DEFER; + + /* CDMA devices don't have problems with the secondary ports, so after + * ensuring we have a device by probing the first port, probe the secondary + * ports on CDMA devices too. + */ + if (existing) + g_object_get (G_OBJECT (existing), MM_MODEM_TYPE, &existing_type, NULL); + + if (usbif == 0 || (existing_type == MM_MODEM_TYPE_CDMA)) { + 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; + } else { + HuaweiSupportsInfo *info; + GError *error = NULL; + + /* Listen for Huawei-specific unsolicited messages */ + info = g_malloc0 (sizeof (HuaweiSupportsInfo)); + + info->serial = mm_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); + + add_regex (info->serial, "\\r\\n\\^RSSI:(\\d+)\\r\\n", task); + add_regex (info->serial, "\\r\\n\\^MODE:(\\d),(\\d)\\r\\n", task); + add_regex (info->serial, "\\r\\n\\^DSFLOWRPT:(.+)\\r\\n", task); + 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); + + g_object_set_data_full (G_OBJECT (task), TAG_SUPPORTS_INFO, + info, huawei_supports_info_destroy); + + if (!mm_serial_port_open (info->serial, &error)) { + g_warning ("%s: (Huawei) %s: couldn't open serial port: (%d) %s", + __func__, name, + error ? error->code : -1, + error && error->message ? error->message : "(unknown)"); + g_clear_error (&error); + huawei_supports_info_destroy (info); + return MM_PLUGIN_SUPPORTS_PORT_UNSUPPORTED; + } + + 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, *physdev = NULL; + MMModem *modem = NULL; + const char *name, *subsys, *devfile, *sysfs_path; + guint32 caps; + guint16 product = 0; + + port = mm_plugin_base_supports_task_get_port (task); + g_assert (port); + + devfile = g_udev_device_get_device_file (port); + if (!devfile) { + g_set_error (error, 0, 0, "Could not get port's sysfs file."); + 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); + + if (!mm_plugin_base_get_device_ids (base, subsys, name, NULL, &product)) { + g_set_error (error, 0, 0, "Could not get modem product ID."); + return NULL; + } + + caps = mm_plugin_base_supports_task_get_probed_capabilities (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))); + } + } else if (caps & CAP_CDMA) { + modem = mm_modem_huawei_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, MM_PORT_TYPE_UNKNOWN, NULL, error)) { + g_object_unref (modem); + return NULL; + } + } + } else { + HuaweiSupportsInfo *info; + 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; + + modem = existing; + if (!mm_modem_grab_port (modem, subsys, name, ptype, NULL, error)) + return NULL; + } + + return modem; +} + +/*****************************************************************************/ + +static void +mm_plugin_huawei_init (MMPluginHuawei *self) +{ + g_signal_connect (self, "probe-result", G_CALLBACK (probe_result), NULL); +} + +static void +mm_plugin_huawei_class_init (MMPluginHuaweiClass *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-huawei.h b/plugins/mm-plugin-huawei.h new file mode 100644 index 0000000..de9294c --- /dev/null +++ b/plugins/mm-plugin-huawei.h @@ -0,0 +1,42 @@ +/* -*- 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_PLUGIN_HUAWEI_H +#define MM_PLUGIN_HUAWEI_H + +#include "mm-plugin.h" +#include "mm-plugin-base.h" + +#define MM_TYPE_PLUGIN_HUAWEI (mm_plugin_huawei_get_type ()) +#define MM_PLUGIN_HUAWEI(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_PLUGIN_HUAWEI, MMPluginHuawei)) +#define MM_PLUGIN_HUAWEI_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_PLUGIN_HUAWEI, MMPluginHuaweiClass)) +#define MM_IS_PLUGIN_HUAWEI(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_PLUGIN_HUAWEI)) +#define MM_IS_PLUGIN_HUAWEI_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_PLUGIN_HUAWEI)) +#define MM_PLUGIN_HUAWEI_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_PLUGIN_HUAWEI, MMPluginHuaweiClass)) + +typedef struct { + MMPluginBase parent; +} MMPluginHuawei; + +typedef struct { + MMPluginBaseClass parent; +} MMPluginHuaweiClass; + +GType mm_plugin_huawei_get_type (void); + +G_MODULE_EXPORT MMPlugin *mm_plugin_create (void); + +#endif /* MM_PLUGIN_HUAWEI_H */ diff --git a/plugins/mm-plugin-longcheer.c b/plugins/mm-plugin-longcheer.c new file mode 100644 index 0000000..5c19b13 --- /dev/null +++ b/plugins/mm-plugin-longcheer.c @@ -0,0 +1,191 @@ +/* -*- 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. + */ + +#include <string.h> +#include <gmodule.h> +#include "mm-plugin-longcheer.h" +#include "mm-generic-gsm.h" +#include "mm-generic-cdma.h" + +G_DEFINE_TYPE (MMPluginLongcheer, mm_plugin_longcheer, 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_LONGCHEER, + MM_PLUGIN_BASE_NAME, "Longcheer", + 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; + 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; + + if (vendor != 0x1c9e) + 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, *physdev = 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_LONGCHEER_PORT_TYPE_MODEM")) + ptype = MM_PORT_TYPE_PRIMARY; + else if (g_udev_device_get_property_as_boolean (port, "ID_MM_LONGCHEER_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_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); + 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))); + } 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 (caps & (MM_PLUGIN_BASE_PORT_CAP_GSM | CAP_CDMA)) { + modem = existing; + if (!mm_modem_grab_port (modem, subsys, name, ptype, NULL, error)) + return NULL; + } + } + + return modem; +} + +/*****************************************************************************/ + +static void +mm_plugin_longcheer_init (MMPluginLongcheer *self) +{ + g_signal_connect (self, "probe-result", G_CALLBACK (probe_result), NULL); +} + +static void +mm_plugin_longcheer_class_init (MMPluginLongcheerClass *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-longcheer.h b/plugins/mm-plugin-longcheer.h new file mode 100644 index 0000000..387a290 --- /dev/null +++ b/plugins/mm-plugin-longcheer.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 Red Hat, Inc. + */ + +#ifndef MM_PLUGIN_LONGCHEER_H +#define MM_PLUGIN_LONGCHEER_H + +#include "mm-plugin-base.h" + +#define MM_TYPE_PLUGIN_LONGCHEER (mm_plugin_longcheer_get_type ()) +#define MM_PLUGIN_LONGCHEER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_PLUGIN_LONGCHEER, MMPluginLongcheer)) +#define MM_PLUGIN_LONGCHEER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_PLUGIN_LONGCHEER, MMPluginLongcheerClass)) +#define MM_IS_PLUGIN_LONGCHEER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_PLUGIN_LONGCHEER)) +#define MM_IS_PLUGIN_LONGCHEER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_PLUGIN_LONGCHEER)) +#define MM_PLUGIN_LONGCHEER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_PLUGIN_LONGCHEER, MMPluginLongcheerClass)) + +typedef struct { + MMPluginBase parent; +} MMPluginLongcheer; + +typedef struct { + MMPluginBaseClass parent; +} MMPluginLongcheerClass; + +GType mm_plugin_longcheer_get_type (void); + +G_MODULE_EXPORT MMPlugin *mm_plugin_create (void); + +#endif /* MM_PLUGIN_LONGCHEER_H */ diff --git a/plugins/mm-plugin-mbm.c b/plugins/mm-plugin-mbm.c new file mode 100644 index 0000000..a380f98 --- /dev/null +++ b/plugins/mm-plugin-mbm.c @@ -0,0 +1,175 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * Copyright (C) 2008 Ericsson AB + * + * Author: Per Hallsmark <per.hallsmark@ericsson.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 + * 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. + * + * 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> +#include <gmodule.h> + +#define G_UDEV_API_IS_SUBJECT_TO_CHANGE +#include <gudev/gudev.h> + +#include "mm-plugin-mbm.h" +#include "mm-modem-mbm.h" + +G_DEFINE_TYPE (MMPluginMbm, mm_plugin_mbm, 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_MBM, + MM_PLUGIN_BASE_NAME, "Ericsson MBM", + NULL)); +} + +/*****************************************************************************/ + +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) +{ + mm_plugin_base_supports_task_complete (task, get_level_for_capabilities (capabilities)); +} + +static MMPluginSupportsResult +supports_port (MMPluginBase *base, + MMModem *existing, + MMPluginBaseSupportsTask *task) +{ + GUdevDevice *port, *physdev; + guint32 cached = 0, level; + const char *driver, *subsys; + + /* Can't do anything with non-serial ports */ + port = mm_plugin_base_supports_task_get_port (task); + g_assert (port); + subsys = g_udev_device_get_subsystem (port); + g_assert (subsys); + + if (strcmp (subsys, "tty") && strcmp (subsys, "net")) + return MM_PLUGIN_SUPPORTS_PORT_UNSUPPORTED; + + driver = mm_plugin_base_supports_task_get_driver (task); + if (!driver) + return MM_PLUGIN_SUPPORTS_PORT_UNSUPPORTED; + + physdev = mm_plugin_base_supports_task_get_physdev (task); + g_assert (physdev); + if (!g_udev_device_get_property_as_boolean (physdev, "ID_MM_ERICSSON_MBM")) + return MM_PLUGIN_SUPPORTS_PORT_UNSUPPORTED; + + if (!strcmp (subsys, "net")) { + mm_plugin_base_supports_task_complete (task, 10); + return MM_PLUGIN_SUPPORTS_PORT_IN_PROGRESS; + } + + 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, *physdev = NULL; + MMModem *modem = NULL; + const char *name, *subsys, *sysfs_path; + guint32 caps; + + 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); + if (!(caps & MM_PLUGIN_BASE_PORT_CAP_GSM) && strcmp (subsys, "net")) + return NULL; + + if (!existing) { + modem = mm_modem_mbm_new (sysfs_path, + mm_plugin_base_supports_task_get_driver (task), + mm_plugin_get_name (MM_PLUGIN (base))); + if (modem) { + if (!mm_modem_grab_port (modem, subsys, name, MM_PORT_TYPE_UNKNOWN, NULL, error)) { + g_object_unref (modem); + return NULL; + } + } + } else { + modem = existing; + if (!mm_modem_grab_port (modem, subsys, name, MM_PORT_TYPE_UNKNOWN, NULL, error)) + return NULL; + } + + return modem; +} + +/*****************************************************************************/ + +static void +mm_plugin_mbm_init (MMPluginMbm *self) +{ + g_signal_connect (self, "probe-result", G_CALLBACK (probe_result), NULL); +} + +static void +mm_plugin_mbm_class_init (MMPluginMbmClass *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-mbm.h b/plugins/mm-plugin-mbm.h new file mode 100644 index 0000000..c0e73b5 --- /dev/null +++ b/plugins/mm-plugin-mbm.h @@ -0,0 +1,48 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * Copyright (C) 2008 Ericsson AB + * + * Author: Per Hallsmark <per.hallsmark@ericsson.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 + * 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. + * + * 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 +#define MM_PLUGIN_MBM_H + +#include "mm-plugin-base.h" +#include "mm-generic-gsm.h" + +#define MM_TYPE_PLUGIN_MBM (mm_plugin_mbm_get_type ()) +#define MM_PLUGIN_MBM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_PLUGIN_MBM, MMPluginMbm)) +#define MM_PLUGIN_MBM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_PLUGIN_MBM, MMPluginMbmClass)) +#define MM_IS_PLUGIN_MBM(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_PLUGIN_MBM)) +#define MM_IS_PLUGIN_MBM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_PLUGIN_MBM)) +#define MM_PLUGIN_MBM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_PLUGIN_MBM, MMPluginMbmClass)) + +typedef struct { + MMPluginBase parent; +} MMPluginMbm; + +typedef struct { + MMPluginBaseClass parent; +} MMPluginMbmClass; + +GType mm_plugin_mbm_get_type (void); + +G_MODULE_EXPORT MMPlugin *mm_plugin_create (void); + +#endif /* MM_PLUGIN_MBM_H */ diff --git a/plugins/mm-plugin-moto-c.c b/plugins/mm-plugin-moto-c.c new file mode 100644 index 0000000..5b32a1e --- /dev/null +++ b/plugins/mm-plugin-moto-c.c @@ -0,0 +1,160 @@ +/* -*- 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. + */ + +#include <string.h> +#include <gmodule.h> +#define G_UDEV_API_IS_SUBJECT_TO_CHANGE +#include <gudev/gudev.h> + +#include "mm-plugin-moto-c.h" +#include "mm-modem-moto-c-gsm.h" + +G_DEFINE_TYPE (MMPluginMotoC, mm_plugin_moto_c, 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_MOTO_C, + MM_PLUGIN_BASE_NAME, "MotoC", + NULL)); +} + +/*****************************************************************************/ + +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); +} + +static MMPluginSupportsResult +supports_port (MMPluginBase *base, + MMModem *existing, + MMPluginBaseSupportsTask *task) +{ + GUdevDevice *port; + const char *tmp; + guint32 cached = 0; + + /* 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; + + tmp = g_udev_device_get_property (port, "ID_BUS"); + if (!tmp || strcmp (tmp, "usb")) + return MM_PLUGIN_SUPPORTS_PORT_UNSUPPORTED; + + tmp = g_udev_device_get_property (port, "ID_VENDOR_ID"); + if (!tmp || strcmp (tmp, "22b8")) + return MM_PLUGIN_SUPPORTS_PORT_UNSUPPORTED; + + tmp = g_udev_device_get_property (port, "ID_MODEL_ID"); + if (!tmp || (strcmp (tmp, "3802") && strcmp (tmp, "4902"))) + 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) { + mm_plugin_base_supports_task_complete (task, 10); + 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, *physdev = NULL; + MMModem *modem = NULL; + const char *name, *subsys, *devfile, *sysfs_path; + + port = mm_plugin_base_supports_task_get_port (task); + g_assert (port); + + devfile = g_udev_device_get_device_file (port); + if (!devfile) { + g_set_error (error, 0, 0, "Could not get port's sysfs file."); + 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); + + if (!existing) { + modem = mm_modem_moto_c_gsm_new (sysfs_path, + mm_plugin_base_supports_task_get_driver (task), + mm_plugin_get_name (MM_PLUGIN (base))); + + if (modem) { + if (!mm_modem_grab_port (modem, subsys, name, MM_PORT_TYPE_UNKNOWN, NULL, error)) { + g_object_unref (modem); + return NULL; + } + } + } else { + modem = existing; + if (!mm_modem_grab_port (modem, subsys, name, MM_PORT_TYPE_UNKNOWN, NULL, error)) + return NULL; + } + + return modem; +} + +/*****************************************************************************/ + +static void +mm_plugin_moto_c_init (MMPluginMotoC *self) +{ + g_signal_connect (self, "probe-result", G_CALLBACK (probe_result), NULL); +} + +static void +mm_plugin_moto_c_class_init (MMPluginMotoCClass *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-moto-c.h b/plugins/mm-plugin-moto-c.h new file mode 100644 index 0000000..8583607 --- /dev/null +++ b/plugins/mm-plugin-moto-c.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_PLUGIN_MOTO_C_H +#define MM_PLUGIN_MOTO_C_H + +#include "mm-plugin.h" +#include "mm-plugin-base.h" +#include "mm-generic-gsm.h" + +#define MM_TYPE_PLUGIN_MOTO_C (mm_plugin_moto_c_get_type ()) +#define MM_PLUGIN_MOTO_C(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_PLUGIN_MOTO_C, MMPluginMotoC)) +#define MM_PLUGIN_MOTO_C_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_PLUGIN_MOTO_C, MMPluginMotoCClass)) +#define MM_IS_PLUGIN_MOTO_C(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_PLUGIN_MOTO_C)) +#define MM_IS_PLUGIN_MOTO_C_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_PLUGIN_MOTO_C)) +#define MM_PLUGIN_MOTO_C_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_PLUGIN_MOTO_C, MMPluginMotoCClass)) + +typedef struct { + MMPluginBase parent; +} MMPluginMotoC; + +typedef struct { + MMPluginBaseClass parent; +} MMPluginMotoCClass; + +GType mm_plugin_moto_c_get_type (void); + +G_MODULE_EXPORT MMPlugin *mm_plugin_create (void); + +#endif /* MM_PLUGIN_MOTO_C_H */ diff --git a/plugins/mm-plugin-nokia.c b/plugins/mm-plugin-nokia.c new file mode 100644 index 0000000..e088323 --- /dev/null +++ b/plugins/mm-plugin-nokia.c @@ -0,0 +1,179 @@ +/* -*- 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. + */ + +#include <string.h> +#include <gmodule.h> +#include "mm-plugin-nokia.h" +#include "mm-modem-nokia.h" +#include "mm-generic-cdma.h" + +G_DEFINE_TYPE (MMPluginNokia, mm_plugin_nokia, 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_NOKIA, + MM_PLUGIN_BASE_NAME, "Nokia", + 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; + 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; + const char *subsys, *name; + guint16 vendor = 0; + + /* 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; + + if (vendor != 0x0421) + 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, *physdev = NULL; + MMModem *modem = NULL; + const char *name, *subsys, *devfile, *sysfs_path; + guint32 caps; + + port = mm_plugin_base_supports_task_get_port (task); + g_assert (port); + + devfile = g_udev_device_get_device_file (port); + if (!devfile) { + g_set_error (error, 0, 0, "Could not get port's sysfs file."); + 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); + if (!existing) { + if (caps & MM_PLUGIN_BASE_PORT_CAP_GSM) { + modem = mm_modem_nokia_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, MM_PORT_TYPE_UNKNOWN, NULL, error)) { + g_object_unref (modem); + 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; + } + } + + return modem; +} + +/*****************************************************************************/ + +static void +mm_plugin_nokia_init (MMPluginNokia *self) +{ + g_signal_connect (self, "probe-result", G_CALLBACK (probe_result), NULL); +} + +static void +mm_plugin_nokia_class_init (MMPluginNokiaClass *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-nokia.h b/plugins/mm-plugin-nokia.h new file mode 100644 index 0000000..fdc0f41 --- /dev/null +++ b/plugins/mm-plugin-nokia.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 Red Hat, Inc. + */ + +#ifndef MM_PLUGIN_NOKIA_H +#define MM_PLUGIN_NOKIA_H + +#include "mm-plugin-base.h" + +#define MM_TYPE_PLUGIN_NOKIA (mm_plugin_nokia_get_type ()) +#define MM_PLUGIN_NOKIA(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_PLUGIN_NOKIA, MMPluginNokia)) +#define MM_PLUGIN_NOKIA_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_PLUGIN_NOKIA, MMPluginNokiaClass)) +#define MM_IS_PLUGIN_NOKIA(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_PLUGIN_NOKIA)) +#define MM_IS_PLUGIN_NOKIA_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_PLUGIN_NOKIA)) +#define MM_PLUGIN_NOKIA_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_PLUGIN_NOKIA, MMPluginNokiaClass)) + +typedef struct { + MMPluginBase parent; +} MMPluginNokia; + +typedef struct { + MMPluginBaseClass parent; +} MMPluginNokiaClass; + +GType mm_plugin_nokia_get_type (void); + +G_MODULE_EXPORT MMPlugin *mm_plugin_create (void); + +#endif /* MM_PLUGIN_NOKIA_H */ diff --git a/plugins/mm-plugin-novatel.c b/plugins/mm-plugin-novatel.c new file mode 100644 index 0000000..48ff7ec --- /dev/null +++ b/plugins/mm-plugin-novatel.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 Red Hat, Inc. + */ + +#include <string.h> +#include <gmodule.h> +#include "mm-plugin-novatel.h" +#include "mm-modem-novatel-gsm.h" +#include "mm-generic-cdma.h" + +G_DEFINE_TYPE (MMPluginNovatel, mm_plugin_novatel, 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_NOVATEL, + MM_PLUGIN_BASE_NAME, "Novatel", + 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; + 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; + const char *subsys, *name, *driver; + guint16 vendor = 0; + + /* 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; + + driver = mm_plugin_base_supports_task_get_driver (task); + if (!driver || (strcmp (driver, "option1") && strcmp (driver, "option"))) + return MM_PLUGIN_SUPPORTS_PORT_UNSUPPORTED; + + if (vendor != 0x1410 && vendor != 0x413c) + 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, *physdev = NULL; + MMModem *modem = NULL; + const char *name, *subsys, *devfile, *sysfs_path; + guint32 caps; + + port = mm_plugin_base_supports_task_get_port (task); + g_assert (port); + + devfile = g_udev_device_get_device_file (port); + if (!devfile) { + g_set_error (error, 0, 0, "Could not get port's sysfs file."); + 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); + 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)); + } + + if (modem) { + if (!mm_modem_grab_port (modem, subsys, name, MM_PORT_TYPE_UNKNOWN, NULL, error)) { + g_object_unref (modem); + 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; + } + } + + return modem; +} + +/*****************************************************************************/ + +static void +mm_plugin_novatel_init (MMPluginNovatel *self) +{ + g_signal_connect (self, "probe-result", G_CALLBACK (probe_result), NULL); +} + +static void +mm_plugin_novatel_class_init (MMPluginNovatelClass *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-novatel.h b/plugins/mm-plugin-novatel.h new file mode 100644 index 0000000..450bbdd --- /dev/null +++ b/plugins/mm-plugin-novatel.h @@ -0,0 +1,42 @@ +/* -*- 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_PLUGIN_NOVATEL_H +#define MM_PLUGIN_NOVATEL_H + +#include "mm-plugin-base.h" +#include "mm-generic-gsm.h" + +#define MM_TYPE_PLUGIN_NOVATEL (mm_plugin_novatel_get_type ()) +#define MM_PLUGIN_NOVATEL(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_PLUGIN_NOVATEL, MMPluginNovatel)) +#define MM_PLUGIN_NOVATEL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_PLUGIN_NOVATEL, MMPluginNovatelClass)) +#define MM_IS_PLUGIN_NOVATEL(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_PLUGIN_NOVATEL)) +#define MM_IS_PLUGIN_NOVATEL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_PLUGIN_NOVATEL)) +#define MM_PLUGIN_NOVATEL_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_PLUGIN_NOVATEL, MMPluginNovatelClass)) + +typedef struct { + MMPluginBase parent; +} MMPluginNovatel; + +typedef struct { + MMPluginBaseClass parent; +} MMPluginNovatelClass; + +GType mm_plugin_novatel_get_type (void); + +G_MODULE_EXPORT MMPlugin *mm_plugin_create (void); + +#endif /* MM_PLUGIN_NOVATEL_H */ diff --git a/plugins/mm-plugin-option.c b/plugins/mm-plugin-option.c new file mode 100644 index 0000000..d4c402d --- /dev/null +++ b/plugins/mm-plugin-option.c @@ -0,0 +1,180 @@ +/* -*- 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. + */ + +#include <string.h> +#include <gmodule.h> +#include "mm-plugin-option.h" +#include "mm-modem-option.h" + +G_DEFINE_TYPE (MMPluginOption, mm_plugin_option, 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_OPTION, + MM_PLUGIN_BASE_NAME, "Option", + NULL)); +} + +/*****************************************************************************/ + +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) +{ + 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; + const char *driver, *subsys, *name; + guint16 vendor = 0; + + /* 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); + + driver = mm_plugin_base_supports_task_get_driver (task); + if (!driver || (strcmp (driver, "option1") && strcmp (driver, "option"))) + return MM_PLUGIN_SUPPORTS_PORT_UNSUPPORTED; + + if (!mm_plugin_base_get_device_ids (base, subsys, name, &vendor, NULL)) + return MM_PLUGIN_SUPPORTS_PORT_UNSUPPORTED; + + if (vendor != 0x0af0) + 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, *physdev = NULL; + MMModem *modem = NULL; + const char *name, *subsys, *devfile, *sysfs_path; + guint32 caps; + int usbif; + MMPortType ptype = MM_PORT_TYPE_SECONDARY; + + port = mm_plugin_base_supports_task_get_port (task); + g_assert (port); + + devfile = g_udev_device_get_device_file (port); + if (!devfile) { + g_set_error (error, 0, 0, "Could not get port's sysfs file."); + 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); + + /* This is the MM equivalent of NM commit 9d7f5b3d084eee2ccfff721c4beca3e3f34bdc50; + * Genuine Option NV devices are always supposed to use USB interface 0 as + * the modem/data port, per mail with Option engineers. Only this port + * will emit responses to dialing commands. + */ + usbif = g_udev_device_get_property_as_int (port, "ID_USB_INTERFACE_NUM"); + if (usbif == 0) + ptype = MM_PORT_TYPE_PRIMARY; + + caps = mm_plugin_base_supports_task_get_probed_capabilities (task); + if (!existing) { + if (caps & MM_PLUGIN_BASE_PORT_CAP_GSM) { + modem = mm_modem_option_new (sysfs_path, + mm_plugin_base_supports_task_get_driver (task), + mm_plugin_get_name (MM_PLUGIN (base))); + } + + if (modem) { + if (!mm_modem_grab_port (modem, subsys, name, ptype, NULL, error)) { + g_object_unref (modem); + 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; + } + } + + return modem; +} + +/*****************************************************************************/ + +static void +mm_plugin_option_init (MMPluginOption *self) +{ + g_signal_connect (self, "probe-result", G_CALLBACK (probe_result), NULL); +} + +static void +mm_plugin_option_class_init (MMPluginOptionClass *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-option.h b/plugins/mm-plugin-option.h new file mode 100644 index 0000000..dfcff74 --- /dev/null +++ b/plugins/mm-plugin-option.h @@ -0,0 +1,42 @@ +/* -*- 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_PLUGIN_OPTION_H +#define MM_PLUGIN_OPTION_H + +#include "mm-plugin-base.h" +#include "mm-generic-gsm.h" + +#define MM_TYPE_PLUGIN_OPTION (mm_plugin_option_get_type ()) +#define MM_PLUGIN_OPTION(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_PLUGIN_OPTION, MMPluginOption)) +#define MM_PLUGIN_OPTION_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_PLUGIN_OPTION, MMPluginOptionClass)) +#define MM_IS_PLUGIN_OPTION(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_PLUGIN_OPTION)) +#define MM_IS_PLUGIN_OPTION_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_PLUGIN_OPTION)) +#define MM_PLUGIN_OPTION_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_PLUGIN_OPTION, MMPluginOptionClass)) + +typedef struct { + MMPluginBase parent; +} MMPluginOption; + +typedef struct { + MMPluginBaseClass parent; +} MMPluginOptionClass; + +GType mm_plugin_option_get_type (void); + +G_MODULE_EXPORT MMPlugin *mm_plugin_create (void); + +#endif /* MM_PLUGIN_OPTION_H */ diff --git a/plugins/mm-plugin-sierra.c b/plugins/mm-plugin-sierra.c new file mode 100644 index 0000000..637f46d --- /dev/null +++ b/plugins/mm-plugin-sierra.c @@ -0,0 +1,204 @@ +/* -*- 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. + */ + +#include <string.h> +#include <gmodule.h> +#include "mm-plugin-sierra.h" +#include "mm-modem-sierra-gsm.h" +#include "mm-modem-sierra-cdma.h" + +G_DEFINE_TYPE (MMPluginSierra, mm_plugin_sierra, 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_SIERRA, + MM_PLUGIN_BASE_NAME, "Sierra", + NULL)); +} + +/*****************************************************************************/ + +#define TAG_SIERRA_SECONDARY_PORT "sierra-secondary-port" + +#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; + return 0; +} + +static void +handle_probe_response (MMPluginBase *self, + MMPluginBaseSupportsTask *task, + const char *cmd, + const char *response, + const GError *error) +{ + if (error || !response || strcmp (cmd, "I")) { + MM_PLUGIN_BASE_CLASS (mm_plugin_sierra_parent_class)->handle_probe_response (self, task, cmd, response, error); + return; + } + + if (strstr (response, "APP1") || strstr (response, "APP2") || strstr (response, "APP3")) { + g_object_set_data (G_OBJECT (task), TAG_SIERRA_SECONDARY_PORT, GUINT_TO_POINTER (TRUE)); + mm_plugin_base_supports_task_complete (task, 10); + return; + } + + /* Not an app port, let the superclass handle the response */ + MM_PLUGIN_BASE_CLASS (mm_plugin_sierra_parent_class)->handle_probe_response (self, task, cmd, response, error); +} + +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; + const char *driver; + + /* 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; + + driver = mm_plugin_base_supports_task_get_driver (task); + if (!driver || strcmp (driver, "sierra")) + 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, *physdev = NULL; + MMModem *modem = NULL; + const char *name, *subsys, *devfile, *sysfs_path; + guint32 caps; + MMPortType ptype = MM_PORT_TYPE_UNKNOWN; + + port = mm_plugin_base_supports_task_get_port (task); + g_assert (port); + + devfile = g_udev_device_get_device_file (port); + if (!devfile) { + g_set_error (error, 0, 0, "Could not get port's sysfs file."); + 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); + + /* Is it a GSM secondary port? */ + if (g_object_get_data (G_OBJECT (task), TAG_SIERRA_SECONDARY_PORT)) + ptype = MM_PORT_TYPE_SECONDARY; + + caps = mm_plugin_base_supports_task_get_probed_capabilities (task); + if (!existing) { + if ((caps & MM_PLUGIN_BASE_PORT_CAP_GSM) || (ptype != MM_PORT_TYPE_UNKNOWN)) { + modem = mm_modem_sierra_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_sierra_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 ( (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; + } + } + + return modem; +} + +/*****************************************************************************/ + +static void +mm_plugin_sierra_init (MMPluginSierra *self) +{ + g_signal_connect (self, "probe-result", G_CALLBACK (probe_result), NULL); +} + +static void +mm_plugin_sierra_class_init (MMPluginSierraClass *klass) +{ + MMPluginBaseClass *pb_class = MM_PLUGIN_BASE_CLASS (klass); + + pb_class->supports_port = supports_port; + pb_class->grab_port = grab_port; + pb_class->handle_probe_response = handle_probe_response; +} diff --git a/plugins/mm-plugin-sierra.h b/plugins/mm-plugin-sierra.h new file mode 100644 index 0000000..01fffc4 --- /dev/null +++ b/plugins/mm-plugin-sierra.h @@ -0,0 +1,42 @@ +/* -*- 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_PLUGIN_SIERRA_H +#define MM_PLUGIN_SIERRA_H + +#include "mm-plugin-base.h" +#include "mm-generic-gsm.h" + +#define MM_TYPE_PLUGIN_SIERRA (mm_plugin_sierra_get_type ()) +#define MM_PLUGIN_SIERRA(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_PLUGIN_SIERRA, MMPluginSierra)) +#define MM_PLUGIN_SIERRA_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_PLUGIN_SIERRA, MMPluginSierraClass)) +#define MM_IS_PLUGIN_SIERRA(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_PLUGIN_SIERRA)) +#define MM_IS_PLUGIN_SIERRA_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_PLUGIN_SIERRA)) +#define MM_PLUGIN_SIERRA_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_PLUGIN_SIERRA, MMPluginSierraClass)) + +typedef struct { + MMPluginBase parent; +} MMPluginSierra; + +typedef struct { + MMPluginBaseClass parent; +} MMPluginSierraClass; + +GType mm_plugin_sierra_get_type (void); + +G_MODULE_EXPORT MMPlugin *mm_plugin_create (void); + +#endif /* MM_PLUGIN_SIERRA_H */ diff --git a/plugins/mm-plugin-zte.c b/plugins/mm-plugin-zte.c new file mode 100644 index 0000000..101fb46 --- /dev/null +++ b/plugins/mm-plugin-zte.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 Red Hat, Inc. + */ + +#include <string.h> +#include <gmodule.h> +#include "mm-plugin-zte.h" +#include "mm-modem-zte.h" +#include "mm-generic-cdma.h" + +G_DEFINE_TYPE (MMPluginZte, mm_plugin_zte, 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_ZTE, + MM_PLUGIN_BASE_NAME, "ZTE", + 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; + 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; + + if (vendor != 0x19d2) + 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 */ + + /* Many ZTE devices will flood the port with "Message waiting" indications + * and eventually fill up the serial buffer and crash. We need to turn off + * that indicator. See NetworkManager commits + * 1235f71b20c92cded4abd976ccc5010649aae1a0 and + * f38ad328acfdc6ce29dd1380602c546b064161ae for more details. + */ + mm_plugin_base_supports_task_set_custom_init_command (task, "ATE0+CPMS?", 3, 4, TRUE); + + 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, *physdev = 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 */ + if (g_udev_device_get_property_as_boolean (port, "ID_MM_ZTE_PORT_TYPE_MODEM")) + ptype = MM_PORT_TYPE_PRIMARY; + 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); + if (!existing) { + if (caps & MM_PLUGIN_BASE_PORT_CAP_GSM) { + modem = mm_modem_zte_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 (caps & (MM_PLUGIN_BASE_PORT_CAP_GSM | CAP_CDMA)) { + modem = existing; + if (!mm_modem_grab_port (modem, subsys, name, ptype, NULL, error)) + return NULL; + } + } + + return modem; +} + +/*****************************************************************************/ + +static void +mm_plugin_zte_init (MMPluginZte *self) +{ + g_signal_connect (self, "probe-result", G_CALLBACK (probe_result), NULL); +} + +static void +mm_plugin_zte_class_init (MMPluginZteClass *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-zte.h b/plugins/mm-plugin-zte.h new file mode 100644 index 0000000..7a4d4c9 --- /dev/null +++ b/plugins/mm-plugin-zte.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 Red Hat, Inc. + */ + +#ifndef MM_PLUGIN_ZTE_H +#define MM_PLUGIN_ZTE_H + +#include "mm-plugin-base.h" + +#define MM_TYPE_PLUGIN_ZTE (mm_plugin_zte_get_type ()) +#define MM_PLUGIN_ZTE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_PLUGIN_ZTE, MMPluginZte)) +#define MM_PLUGIN_ZTE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_PLUGIN_ZTE, MMPluginZteClass)) +#define MM_IS_PLUGIN_ZTE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_PLUGIN_ZTE)) +#define MM_IS_PLUGIN_ZTE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_PLUGIN_ZTE)) +#define MM_PLUGIN_ZTE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_PLUGIN_ZTE, MMPluginZteClass)) + +typedef struct { + MMPluginBase parent; +} MMPluginZte; + +typedef struct { + MMPluginBaseClass parent; +} MMPluginZteClass; + +GType mm_plugin_zte_get_type (void); + +G_MODULE_EXPORT MMPlugin *mm_plugin_create (void); + +#endif /* MM_PLUGIN_ZTE_H */ diff --git a/src/Makefile.am b/src/Makefile.am new file mode 100644 index 0000000..9209b55 --- /dev/null +++ b/src/Makefile.am @@ -0,0 +1,99 @@ +SUBDIRS=. tests + +noinst_LTLIBRARIES = libmodem-helpers.la + +libmodem_helpers_la_CPPFLAGS = \ + $(MM_CFLAGS) + +libmodem_helpers_la_SOURCES = \ + mm-errors.c \ + mm-errors.h \ + mm-modem-helpers.c \ + mm-modem-helpers.h + +sbin_PROGRAMS = modem-manager + +modem_manager_CPPFLAGS = \ + $(MM_CFLAGS) \ + $(GUDEV_CFLAGS) \ + -I${top_builddir}/marshallers \ + -DPLUGINDIR=\"$(pkglibdir)\" + +modem_manager_LDADD = \ + $(MM_LIBS) \ + $(GUDEV_LIBS) \ + $(top_builddir)/marshallers/libmarshallers.la \ + $(builddir)/libmodem-helpers.la + +modem_manager_SOURCES = \ + main.c \ + mm-callback-info.c \ + mm-callback-info.h \ + mm-manager.c \ + mm-manager.h \ + mm-modem.c \ + mm-modem.h \ + mm-port.c \ + mm-port.h \ + mm-modem-base.c \ + mm-modem-base.h \ + mm-serial-port.c \ + mm-serial-port.h \ + mm-serial-parsers.c \ + mm-serial-parsers.h \ + mm-generic-cdma.c \ + mm-generic-cdma.h \ + mm-generic-gsm.c \ + mm-generic-gsm.h \ + mm-modem-cdma.c \ + mm-modem-cdma.h \ + mm-modem-gsm.h \ + mm-modem-gsm-card.c \ + mm-modem-gsm-card.h \ + mm-modem-gsm-network.c \ + mm-modem-gsm-network.h \ + mm-modem-gsm-sms.c \ + mm-modem-gsm-sms.h \ + mm-modem-simple.c \ + mm-modem-simple.h \ + mm-options.c \ + mm-options.h \ + mm-plugin.c \ + mm-plugin.h \ + mm-plugin-base.c \ + mm-plugin-base.h \ + mm-properties-changed-signal.c \ + mm-properties-changed-signal.h + +mm-manager-glue.h: $(top_srcdir)/introspection/mm-manager.xml + dbus-binding-tool --prefix=mm_manager --mode=glib-server --output=$@ $< + +mm-modem-glue.h: $(top_srcdir)/introspection/mm-modem.xml + dbus-binding-tool --prefix=mm_modem --mode=glib-server --output=$@ $< + +mm-modem-simple-glue.h: $(top_srcdir)/introspection/mm-modem-simple.xml + dbus-binding-tool --prefix=mm_modem_simple --mode=glib-server --output=$@ $< + +mm-modem-cdma-glue.h: $(top_srcdir)/introspection/mm-modem-cdma.xml + dbus-binding-tool --prefix=mm_modem_cdma --mode=glib-server --output=$@ $< + +mm-modem-gsm-card-glue.h: $(top_srcdir)/introspection/mm-modem-gsm-card.xml + dbus-binding-tool --prefix=mm_modem_gsm_card --mode=glib-server --output=$@ $< + +mm-modem-gsm-network-glue.h: $(top_srcdir)/introspection/mm-modem-gsm-network.xml + dbus-binding-tool --prefix=mm_modem_gsm_network --mode=glib-server --output=$@ $< + +mm-modem-gsm-sms-glue.h: $(top_srcdir)/introspection/mm-modem-gsm-sms.xml + dbus-binding-tool --prefix=mm_modem_gsm_sms --mode=glib-server --output=$@ $< + + +BUILT_SOURCES = \ + mm-manager-glue.h \ + mm-modem-glue.h \ + mm-modem-simple-glue.h \ + mm-modem-cdma-glue.h \ + mm-modem-gsm-card-glue.h \ + mm-modem-gsm-network-glue.h \ + mm-modem-gsm-sms-glue.h + +CLEANFILES = $(BUILT_SOURCES) diff --git a/src/main.c b/src/main.c new file mode 100644 index 0000000..3669115 --- /dev/null +++ b/src/main.c @@ -0,0 +1,211 @@ +/* -*- 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. + */ + +#include <signal.h> +#include <syslog.h> +#include <string.h> +#include <dbus/dbus-glib.h> +#include <dbus/dbus-glib-lowlevel.h> +#include "mm-manager.h" +#include "mm-options.h" + +#define HAL_DBUS_SERVICE "org.freedesktop.Hal" + +static GMainLoop *loop = NULL; + +static void +mm_signal_handler (int signo) +{ + if (signo == SIGUSR1) + mm_options_set_debug (!mm_options_debug ()); + else if (signo == SIGINT || signo == SIGTERM) { + g_message ("Caught signal %d, shutting down...", signo); + g_main_loop_quit (loop); + } +} + +static void +setup_signals (void) +{ + struct sigaction action; + sigset_t mask; + + sigemptyset (&mask); + action.sa_handler = mm_signal_handler; + action.sa_mask = mask; + action.sa_flags = 0; + sigaction (SIGUSR1, &action, NULL); + sigaction (SIGTERM, &action, NULL); + sigaction (SIGINT, &action, NULL); +} + +static void +log_handler (const gchar *log_domain, + GLogLevelFlags log_level, + const gchar *message, + gpointer ignored) +{ + int syslog_priority; + + switch (log_level) { + case G_LOG_LEVEL_ERROR: + syslog_priority = LOG_CRIT; + break; + + case G_LOG_LEVEL_CRITICAL: + syslog_priority = LOG_ERR; + break; + + case G_LOG_LEVEL_WARNING: + syslog_priority = LOG_WARNING; + break; + + case G_LOG_LEVEL_MESSAGE: + syslog_priority = LOG_NOTICE; + break; + + case G_LOG_LEVEL_DEBUG: + syslog_priority = LOG_DEBUG; + break; + + case G_LOG_LEVEL_INFO: + default: + syslog_priority = LOG_INFO; + break; + } + + syslog (syslog_priority, "%s", message); +} + + +static void +logging_setup (void) +{ + openlog (G_LOG_DOMAIN, LOG_CONS, LOG_DAEMON); + g_log_set_handler (G_LOG_DOMAIN, + G_LOG_LEVEL_MASK | G_LOG_FLAG_FATAL | G_LOG_FLAG_RECURSION, + log_handler, + NULL); +} + +static void +logging_shutdown (void) +{ + closelog (); +} + +static void +destroy_cb (DBusGProxy *proxy, gpointer user_data) +{ + g_message ("disconnected from the system bus, exiting."); + g_main_loop_quit (loop); +} + +static DBusGProxy * +create_dbus_proxy (DBusGConnection *bus) +{ + DBusGProxy *proxy; + GError *err = NULL; + int request_name_result; + + proxy = dbus_g_proxy_new_for_name (bus, + "org.freedesktop.DBus", + "/org/freedesktop/DBus", + "org.freedesktop.DBus"); + + if (!dbus_g_proxy_call (proxy, "RequestName", &err, + G_TYPE_STRING, MM_DBUS_SERVICE, + G_TYPE_UINT, DBUS_NAME_FLAG_DO_NOT_QUEUE, + G_TYPE_INVALID, + G_TYPE_UINT, &request_name_result, + G_TYPE_INVALID)) { + g_warning ("Could not acquire the %s service.\n" + " Message: '%s'", MM_DBUS_SERVICE, err->message); + + g_error_free (err); + g_object_unref (proxy); + proxy = NULL; + } else if (request_name_result != DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER) { + g_warning ("Could not acquire the " MM_DBUS_SERVICE + " service as it is already taken. Return: %d", + request_name_result); + + g_object_unref (proxy); + proxy = NULL; + } else { + dbus_g_proxy_add_signal (proxy, "NameOwnerChanged", + G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, + G_TYPE_INVALID); + } + + return proxy; +} + +static gboolean +start_manager (gpointer user_data) +{ + mm_manager_start (MM_MANAGER (user_data)); + return FALSE; +} + +int +main (int argc, char *argv[]) +{ + DBusGConnection *bus; + DBusGProxy *proxy; + MMManager *manager; + GError *err = NULL; + guint id; + + mm_options_parse (argc, argv); + g_type_init (); + + setup_signals (); + + if (!mm_options_debug ()) + logging_setup (); + + bus = dbus_g_bus_get (DBUS_BUS_SYSTEM, &err); + if (!bus) { + g_warning ("Could not get the system bus. Make sure " + "the message bus daemon is running! Message: %s", + err->message); + g_error_free (err); + return -1; + } + + proxy = create_dbus_proxy (bus); + if (!proxy) + return -1; + + manager = mm_manager_new (bus); + g_idle_add (start_manager, manager); + + loop = g_main_loop_new (NULL, FALSE); + id = g_signal_connect (proxy, "destroy", G_CALLBACK (destroy_cb), loop); + + g_main_loop_run (loop); + + g_signal_handler_disconnect (proxy, id); + + g_object_unref (manager); + g_object_unref (proxy); + dbus_g_connection_unref (bus); + + logging_shutdown (); + + return 0; +} diff --git a/src/mm-callback-info.c b/src/mm-callback-info.c new file mode 100644 index 0000000..1882898 --- /dev/null +++ b/src/mm-callback-info.c @@ -0,0 +1,210 @@ +/* -*- 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 Novell, Inc. + * Copyright (C) 2009 - 2010 Red Hat, Inc. + */ + +#include "mm-callback-info.h" +#include "mm-errors.h" + +#define CALLBACK_INFO_RESULT "callback-info-result" + +static void +invoke_mm_modem_fn (MMCallbackInfo *info) +{ + MMModemFn callback = (MMModemFn) info->callback; + + callback (info->modem, info->error, info->user_data); +} + +static void +invoke_mm_modem_uint_fn (MMCallbackInfo *info) +{ + MMModemUIntFn callback = (MMModemUIntFn) info->callback; + + callback (info->modem, + GPOINTER_TO_UINT (mm_callback_info_get_data (info, CALLBACK_INFO_RESULT)), + info->error, info->user_data); +} + +static void +invoke_mm_modem_string_fn (MMCallbackInfo *info) +{ + MMModemStringFn callback = (MMModemStringFn) info->callback; + + callback (info->modem, + (const char *) mm_callback_info_get_data (info, CALLBACK_INFO_RESULT), + info->error, info->user_data); +} + + +static void +modem_destroyed_cb (gpointer data, GObject *destroyed) +{ + MMCallbackInfo *info = data; + + info->modem = NULL; + if (!info->pending_id) { + info->error = g_error_new_literal (MM_MODEM_ERROR, + MM_MODEM_ERROR_REMOVED, + "The modem was removed."); + mm_callback_info_schedule (info); + } +} + +static void +callback_info_done (gpointer user_data) +{ + MMCallbackInfo *info = (MMCallbackInfo *) user_data; + + info->pending_id = 0; + info->called = TRUE; + + if (info->invoke_fn && info->callback) + info->invoke_fn (info); + + mm_callback_info_unref (info); +} + +static gboolean +callback_info_do (gpointer user_data) +{ + /* Nothing here, everything is done in callback_info_done to make sure the info->callback + always gets called, even if the pending call gets cancelled. */ + return FALSE; +} + +void +mm_callback_info_schedule (MMCallbackInfo *info) +{ + g_return_if_fail (info != NULL); + g_return_if_fail (info->pending_id == 0); + g_return_if_fail (info->called == FALSE); + + info->pending_id = g_idle_add_full (G_PRIORITY_DEFAULT_IDLE, callback_info_do, info, callback_info_done); +} + +MMCallbackInfo * +mm_callback_info_new_full (MMModem *modem, + MMCallbackInfoInvokeFn invoke_fn, + GCallback callback, + gpointer user_data) +{ + MMCallbackInfo *info; + + g_return_val_if_fail (modem != NULL, NULL); + + info = g_slice_new0 (MMCallbackInfo); + g_datalist_init (&info->qdata); + info->modem = modem; + g_object_weak_ref (G_OBJECT (modem), modem_destroyed_cb, info); + info->invoke_fn = invoke_fn; + info->callback = callback; + info->user_data = user_data; + info->refcount = 1; + + return info; +} + +MMCallbackInfo * +mm_callback_info_new (MMModem *modem, MMModemFn callback, gpointer user_data) +{ + g_return_val_if_fail (modem != NULL, NULL); + + return mm_callback_info_new_full (modem, invoke_mm_modem_fn, (GCallback) callback, user_data); +} + +MMCallbackInfo * +mm_callback_info_uint_new (MMModem *modem, + MMModemUIntFn callback, + gpointer user_data) +{ + g_return_val_if_fail (modem != NULL, NULL); + + return mm_callback_info_new_full (modem, invoke_mm_modem_uint_fn, (GCallback) callback, user_data); +} + +MMCallbackInfo * +mm_callback_info_string_new (MMModem *modem, + MMModemStringFn callback, + gpointer user_data) +{ + g_return_val_if_fail (modem != NULL, NULL); + + return mm_callback_info_new_full (modem, invoke_mm_modem_string_fn, (GCallback) callback, user_data); +} + +void +mm_callback_info_set_result (MMCallbackInfo *info, + gpointer data, + GDestroyNotify destroy) +{ + g_return_if_fail (info != NULL); + + mm_callback_info_set_data (info, CALLBACK_INFO_RESULT, data, destroy); +} + +void +mm_callback_info_set_data (MMCallbackInfo *info, + const char *key, + gpointer data, + GDestroyNotify destroy) +{ + g_return_if_fail (info != NULL); + g_return_if_fail (key != NULL); + + g_datalist_id_set_data_full (&info->qdata, g_quark_from_string (key), data, + data ? destroy : (GDestroyNotify) NULL); +} + +gpointer +mm_callback_info_get_data (MMCallbackInfo *info, const char *key) +{ + GQuark quark; + + g_return_val_if_fail (info != NULL, NULL); + g_return_val_if_fail (key != NULL, NULL); + + quark = g_quark_try_string (key); + + return quark ? g_datalist_id_get_data (&info->qdata, quark) : NULL; +} + +MMCallbackInfo * +mm_callback_info_ref (MMCallbackInfo *info) +{ + g_return_val_if_fail (info != NULL, NULL); + g_return_val_if_fail (info->refcount > 0, NULL); + + info->refcount++; + return info; +} + +void +mm_callback_info_unref (MMCallbackInfo *info) +{ + g_return_if_fail (info != NULL); + + info->refcount--; + if (info->refcount == 0) { + if (info->error) + g_error_free (info->error); + + if (info->modem) + g_object_weak_unref (G_OBJECT (info->modem), modem_destroyed_cb, info); + + g_datalist_clear (&info->qdata); + g_slice_free (MMCallbackInfo, info); + } +} + diff --git a/src/mm-callback-info.h b/src/mm-callback-info.h new file mode 100644 index 0000000..66c2048 --- /dev/null +++ b/src/mm-callback-info.h @@ -0,0 +1,74 @@ +/* -*- 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 Novell, Inc. + */ + +#ifndef MM_CALLBACK_INFO_H +#define MM_CALLBACK_INFO_H + +#include "mm-modem.h" + +typedef struct _MMCallbackInfo MMCallbackInfo; + +typedef void (*MMCallbackInfoInvokeFn) (MMCallbackInfo *info); + +struct _MMCallbackInfo { + guint32 refcount; + + GData *qdata; + MMModem *modem; + + MMCallbackInfoInvokeFn invoke_fn; + GCallback callback; + gboolean called; + + gpointer user_data; + GError *error; + guint pending_id; +}; + +MMCallbackInfo *mm_callback_info_new_full (MMModem *modem, + MMCallbackInfoInvokeFn invoke_fn, + GCallback callback, + gpointer user_data); + +MMCallbackInfo *mm_callback_info_new (MMModem *modem, + MMModemFn callback, + gpointer user_data); + +MMCallbackInfo *mm_callback_info_uint_new (MMModem *modem, + MMModemUIntFn callback, + gpointer user_data); + +MMCallbackInfo *mm_callback_info_string_new (MMModem *modem, + MMModemStringFn callback, + gpointer user_data); + +void mm_callback_info_schedule (MMCallbackInfo *info); +void mm_callback_info_set_result (MMCallbackInfo *info, + gpointer data, + GDestroyNotify destroy); + +void mm_callback_info_set_data (MMCallbackInfo *info, + const char *key, + gpointer data, + GDestroyNotify destroy); + +gpointer mm_callback_info_get_data (MMCallbackInfo *info, + const char *key); + +MMCallbackInfo *mm_callback_info_ref (MMCallbackInfo *info); +void mm_callback_info_unref (MMCallbackInfo *info); + +#endif /* MM_CALLBACK_INFO_H */ + diff --git a/src/mm-errors.c b/src/mm-errors.c new file mode 100644 index 0000000..34f56c1 --- /dev/null +++ b/src/mm-errors.c @@ -0,0 +1,290 @@ +/* -*- 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 Novell, Inc. + * Copyright (C) 2009 - 2010 Red Hat, Inc. + */ + +#include "mm-errors.h" + +#define ENUM_ENTRY(NAME, DESC) { NAME, "" #NAME "", DESC } + +GQuark +mm_serial_error_quark (void) +{ + static GQuark ret = 0; + + if (ret == 0) + ret = g_quark_from_static_string ("mm_serial_error"); + + return ret; +} + +GType +mm_serial_error_get_type (void) +{ + static GType etype = 0; + + if (etype == 0) { + static const GEnumValue values[] = { + ENUM_ENTRY (MM_SERIAL_OPEN_FAILED, "SerialOpenFailed"), + ENUM_ENTRY (MM_SERIAL_SEND_FAILED, "SerialSendfailed"), + ENUM_ENTRY (MM_SERIAL_RESPONSE_TIMEOUT, "SerialResponseTimeout"), + ENUM_ENTRY (MM_SERIAL_OPEN_FAILED_NO_DEVICE, "SerialOpenFailedNoDevice"), + { 0, 0, 0 } + }; + + etype = g_enum_register_static ("MMSerialError", values); + } + + return etype; +} + +GQuark +mm_modem_error_quark (void) +{ + static GQuark ret = 0; + + if (ret == 0) + ret = g_quark_from_static_string ("mm_modem_error"); + + return ret; +} + +GType +mm_modem_error_get_type (void) +{ + static GType etype = 0; + + if (etype == 0) { + static const GEnumValue values[] = { + ENUM_ENTRY (MM_MODEM_ERROR_GENERAL, "General"), + ENUM_ENTRY (MM_MODEM_ERROR_OPERATION_NOT_SUPPORTED, "OperationNotSupported"), + ENUM_ENTRY (MM_MODEM_ERROR_CONNECTED, "Connected"), + ENUM_ENTRY (MM_MODEM_ERROR_DISCONNECTED, "Disconnected"), + ENUM_ENTRY (MM_MODEM_ERROR_OPERATION_IN_PROGRESS, "OperationInProgress"), + ENUM_ENTRY (MM_MODEM_ERROR_REMOVED, "Removed"), + { 0, 0, 0 } + }; + + etype = g_enum_register_static ("MMModemError", values); + } + + return etype; +} + +GQuark +mm_modem_connect_error_quark (void) +{ + static GQuark ret = 0; + + if (ret == 0) + ret = g_quark_from_static_string ("mm_modem_connect_error"); + + return ret; +} + +GType +mm_modem_connect_error_get_type (void) +{ + static GType etype = 0; + + if (etype == 0) { + static const GEnumValue values[] = { + ENUM_ENTRY (MM_MODEM_CONNECT_ERROR_NO_CARRIER, "NoCarrier"), + ENUM_ENTRY (MM_MODEM_CONNECT_ERROR_NO_DIALTONE, "NoDialtone"), + ENUM_ENTRY (MM_MODEM_CONNECT_ERROR_BUSY, "Busy"), + ENUM_ENTRY (MM_MODEM_CONNECT_ERROR_NO_ANSWER, "NoAnswer"), + { 0, 0, 0 } + }; + + etype = g_enum_register_static ("MMModemConnectError", values); + } + + return etype; +} + +GError * +mm_modem_connect_error_for_code (int error_code) +{ + const char *msg; + + switch (error_code) { + case MM_MODEM_CONNECT_ERROR_NO_CARRIER: + msg = "No carrier"; + break; + case MM_MODEM_CONNECT_ERROR_NO_DIALTONE: + msg = "No dialtone"; + break; + case MM_MODEM_CONNECT_ERROR_BUSY: + msg = "Busy"; + break; + case MM_MODEM_CONNECT_ERROR_NO_ANSWER: + msg = "No answer"; + break; + + default: + g_warning ("Invalid error code"); + /* uhm... make something up (yes, ok, lie!). */ + error_code = MM_MODEM_CONNECT_ERROR_NO_CARRIER; + msg = "No carrier"; + } + + return g_error_new_literal (MM_MODEM_CONNECT_ERROR, error_code, msg); +} + + +GQuark +mm_mobile_error_quark (void) +{ + static GQuark ret = 0; + + if (ret == 0) + ret = g_quark_from_static_string ("mm_mobile_error"); + + return ret; +} + +GType +mm_mobile_error_get_type (void) +{ + static GType etype = 0; + + if (etype == 0) { + static const GEnumValue values[] = { + ENUM_ENTRY (MM_MOBILE_ERROR_PHONE_FAILURE, "PhoneFailure"), + ENUM_ENTRY (MM_MOBILE_ERROR_NO_CONNECTION, "NoConnection"), + ENUM_ENTRY (MM_MOBILE_ERROR_LINK_RESERVED, "LinkReserved"), + ENUM_ENTRY (MM_MOBILE_ERROR_NOT_ALLOWED, "OperationNotAllowed"), + ENUM_ENTRY (MM_MOBILE_ERROR_NOT_SUPPORTED, "OperationNotSupported"), + ENUM_ENTRY (MM_MOBILE_ERROR_PH_SIM_PIN, "PhSimPinRequired"), + ENUM_ENTRY (MM_MOBILE_ERROR_PH_FSIM_PIN, "PhFSimPinRequired"), + ENUM_ENTRY (MM_MOBILE_ERROR_PH_FSIM_PUK, "PhFSimPukRequired"), + ENUM_ENTRY (MM_MOBILE_ERROR_SIM_NOT_INSERTED, "SimNotInserted"), + ENUM_ENTRY (MM_MOBILE_ERROR_SIM_PIN, "SimPinRequired"), + ENUM_ENTRY (MM_MOBILE_ERROR_SIM_PUK, "SimPukRequired"), + ENUM_ENTRY (MM_MOBILE_ERROR_SIM_FAILURE, "SimFailure"), + ENUM_ENTRY (MM_MOBILE_ERROR_SIM_BUSY, "SimBusy"), + ENUM_ENTRY (MM_MOBILE_ERROR_SIM_WRONG, "SimWrong"), + ENUM_ENTRY (MM_MOBILE_ERROR_WRONG_PASSWORD, "IncorrectPassword"), + ENUM_ENTRY (MM_MOBILE_ERROR_SIM_PIN2, "SimPin2Required"), + ENUM_ENTRY (MM_MOBILE_ERROR_SIM_PUK2, "SimPuk2Required"), + ENUM_ENTRY (MM_MOBILE_ERROR_MEMORY_FULL, "MemoryFull"), + ENUM_ENTRY (MM_MOBILE_ERROR_INVALID_INDEX, "InvalidIndex"), + ENUM_ENTRY (MM_MOBILE_ERROR_NOT_FOUND, "NotFound"), + ENUM_ENTRY (MM_MOBILE_ERROR_MEMORY_FAILURE, "MemoryFailure"), + ENUM_ENTRY (MM_MOBILE_ERROR_TEXT_TOO_LONG, "TextTooLong"), + ENUM_ENTRY (MM_MOBILE_ERROR_INVALID_CHARS, "InvalidChars"), + ENUM_ENTRY (MM_MOBILE_ERROR_DIAL_STRING_TOO_LONG, "DialStringTooLong"), + ENUM_ENTRY (MM_MOBILE_ERROR_DIAL_STRING_INVALID, "InvalidDialString"), + ENUM_ENTRY (MM_MOBILE_ERROR_NO_NETWORK, "NoNetwork"), + ENUM_ENTRY (MM_MOBILE_ERROR_NETWORK_TIMEOUT, "NetworkTimeout"), + ENUM_ENTRY (MM_MOBILE_ERROR_NETWORK_NOT_ALLOWED, "NetworkNotAllowed"), + ENUM_ENTRY (MM_MOBILE_ERROR_NETWORK_PIN, "NetworkPinRequired"), + ENUM_ENTRY (MM_MOBILE_ERROR_NETWORK_PUK, "NetworkPukRequired"), + ENUM_ENTRY (MM_MOBILE_ERROR_NETWORK_SUBSET_PIN, "NetworkSubsetPinRequired"), + ENUM_ENTRY (MM_MOBILE_ERROR_NETWORK_SUBSET_PUK, "NetworkSubsetPukRequired"), + ENUM_ENTRY (MM_MOBILE_ERROR_SERVICE_PIN, "ServicePinRequired"), + ENUM_ENTRY (MM_MOBILE_ERROR_SERVICE_PUK, "ServicePukRequired"), + ENUM_ENTRY (MM_MOBILE_ERROR_CORP_PIN, "CorporatePinRequired"), + ENUM_ENTRY (MM_MOBILE_ERROR_CORP_PUK, "CorporatePukRequired"), + ENUM_ENTRY (MM_MOBILE_ERROR_HIDDEN_KEY, "HiddenKeyRequired"), + ENUM_ENTRY (MM_MOBILE_ERROR_EAP_NOT_SUPPORTED, "EapMethodNotSupported"), + ENUM_ENTRY (MM_MOBILE_ERROR_INCORRECT_PARAMS, "IncorrectParams"), + ENUM_ENTRY (MM_MOBILE_ERROR_UNKNOWN, "Unknown"), + ENUM_ENTRY (MM_MOBILE_ERROR_GPRS_ILLEGAL_MS, "GprsIllegalMs"), + ENUM_ENTRY (MM_MOBILE_ERROR_GPRS_ILLEGAL_ME, "GprsIllegalMe"), + ENUM_ENTRY (MM_MOBILE_ERROR_GPRS_SERVICE_NOT_ALLOWED, "GprsServiceNotAllowed"), + ENUM_ENTRY (MM_MOBILE_ERROR_GPRS_PLMN_NOT_ALLOWED, "GprsPlmnNotAllowed"), + ENUM_ENTRY (MM_MOBILE_ERROR_GPRS_LOCATION_NOT_ALLOWED, "GprsLocationNotAllowed"), + ENUM_ENTRY (MM_MOBILE_ERROR_GPRS_ROAMING_NOT_ALLOWED, "GprsRoamingNotAllowed"), + ENUM_ENTRY (MM_MOBILE_ERROR_GPRS_OPTION_NOT_SUPPORTED, "GprsOptionNotSupported"), + ENUM_ENTRY (MM_MOBILE_ERROR_GPRS_NOT_SUBSCRIBED, "GprsNotSubscribed"), + ENUM_ENTRY (MM_MOBILE_ERROR_GPRS_OUT_OF_ORDER, "GprsOutOfOrder"), + ENUM_ENTRY (MM_MOBILE_ERROR_GPRS_PDP_AUTH_FAILURE, "GprsPdpAuthFailure"), + ENUM_ENTRY (MM_MOBILE_ERROR_GPRS_UNKNOWN, "GprsUnspecified"), + ENUM_ENTRY (MM_MOBILE_ERROR_GPRS_INVALID_CLASS, "GprsInvalidClass"), + { 0, 0, 0 } + }; + + etype = g_enum_register_static ("MMMobileError", values); + } + + return etype; +} + +GError * +mm_mobile_error_for_code (int error_code) +{ + const char *msg; + + switch (error_code) { + case MM_MOBILE_ERROR_PHONE_FAILURE: msg = "Phone failure"; break; + case MM_MOBILE_ERROR_NO_CONNECTION: msg = "No connection to phone"; break; + case MM_MOBILE_ERROR_LINK_RESERVED: msg = "Phone-adaptor link reserved"; break; + case MM_MOBILE_ERROR_NOT_ALLOWED: msg = "Operation not allowed"; break; + case MM_MOBILE_ERROR_NOT_SUPPORTED: msg = "Operation not supported"; break; + case MM_MOBILE_ERROR_PH_SIM_PIN: msg = "PH-SIM PIN required"; break; + case MM_MOBILE_ERROR_PH_FSIM_PIN: msg = "PH-FSIM PIN required"; break; + case MM_MOBILE_ERROR_PH_FSIM_PUK: msg = "PH-FSIM PUK required"; break; + case MM_MOBILE_ERROR_SIM_NOT_INSERTED: msg = "SIM not inserted"; break; + case MM_MOBILE_ERROR_SIM_PIN: msg = "SIM PIN required"; break; + case MM_MOBILE_ERROR_SIM_PUK: msg = "SIM PUK required"; break; + case MM_MOBILE_ERROR_SIM_FAILURE: msg = "SIM failure"; break; + case MM_MOBILE_ERROR_SIM_BUSY: msg = "SIM busy"; break; + case MM_MOBILE_ERROR_SIM_WRONG: msg = "SIM wrong"; break; + case MM_MOBILE_ERROR_WRONG_PASSWORD: msg = "Incorrect password"; break; + case MM_MOBILE_ERROR_SIM_PIN2: msg = "SIM PIN2 required"; break; + case MM_MOBILE_ERROR_SIM_PUK2: msg = "SIM PUK2 required"; break; + case MM_MOBILE_ERROR_MEMORY_FULL: msg = "Memory full"; break; + case MM_MOBILE_ERROR_INVALID_INDEX: msg = "Invalid index"; break; + case MM_MOBILE_ERROR_NOT_FOUND: msg = "Not found"; break; + case MM_MOBILE_ERROR_MEMORY_FAILURE: msg = "Memory failure"; break; + case MM_MOBILE_ERROR_TEXT_TOO_LONG: msg = "Text string too long"; break; + case MM_MOBILE_ERROR_INVALID_CHARS: msg = "Invalid characters in text string"; break; + case MM_MOBILE_ERROR_DIAL_STRING_TOO_LONG: msg = "Dial string too long"; break; + case MM_MOBILE_ERROR_DIAL_STRING_INVALID: msg = "Invalid characters in dial string"; break; + case MM_MOBILE_ERROR_NO_NETWORK: msg = "No network service"; break; + case MM_MOBILE_ERROR_NETWORK_TIMEOUT: msg = "Network timeout"; break; + case MM_MOBILE_ERROR_NETWORK_NOT_ALLOWED: msg = "Network not allowed - emergency calls only"; break; + case MM_MOBILE_ERROR_NETWORK_PIN: msg = "Network personalization PIN required"; break; + case MM_MOBILE_ERROR_NETWORK_PUK: msg = "Network personalization PUK required"; break; + case MM_MOBILE_ERROR_NETWORK_SUBSET_PIN: msg = "Network subset personalization PIN required"; break; + case MM_MOBILE_ERROR_NETWORK_SUBSET_PUK: msg = "Network subset personalization PUK required"; break; + case MM_MOBILE_ERROR_SERVICE_PIN: msg = "Service provider personalization PIN required"; break; + case MM_MOBILE_ERROR_SERVICE_PUK: msg = "Service provider personalization PUK required"; break; + case MM_MOBILE_ERROR_CORP_PIN: msg = "Corporate personalization PIN required"; break; + case MM_MOBILE_ERROR_CORP_PUK: msg = "Corporate personalization PUK required"; break; + case MM_MOBILE_ERROR_HIDDEN_KEY: msg = "Hidden key required"; break; + case MM_MOBILE_ERROR_EAP_NOT_SUPPORTED: msg = "EAP method not supported"; break; + case MM_MOBILE_ERROR_INCORRECT_PARAMS: msg = "Incorrect parameters"; break; + case MM_MOBILE_ERROR_UNKNOWN: msg = "Unknown error"; break; + case MM_MOBILE_ERROR_GPRS_ILLEGAL_MS: msg = "Illegal MS"; break; + case MM_MOBILE_ERROR_GPRS_ILLEGAL_ME: msg = "Illegal ME"; break; + case MM_MOBILE_ERROR_GPRS_SERVICE_NOT_ALLOWED: msg = "GPRS services not allowed"; break; + case MM_MOBILE_ERROR_GPRS_PLMN_NOT_ALLOWED: msg = "PLMN not allowed"; break; + case MM_MOBILE_ERROR_GPRS_LOCATION_NOT_ALLOWED: msg = "Location area not allowed"; break; + case MM_MOBILE_ERROR_GPRS_ROAMING_NOT_ALLOWED: msg = "Roaming not allowed in this location area"; break; + case MM_MOBILE_ERROR_GPRS_OPTION_NOT_SUPPORTED: msg = "Service option not supported"; break; + case MM_MOBILE_ERROR_GPRS_NOT_SUBSCRIBED: msg = "Requested service option not subscribed"; break; + case MM_MOBILE_ERROR_GPRS_OUT_OF_ORDER: msg = "Service option temporarily out of order"; break; + case MM_MOBILE_ERROR_GPRS_PDP_AUTH_FAILURE: msg = "PDP authentication failure"; break; + case MM_MOBILE_ERROR_GPRS_UNKNOWN: msg = "Unspecified GPRS error"; break; + case MM_MOBILE_ERROR_GPRS_INVALID_CLASS: msg = "Invalid mobile class"; break; + default: + g_warning ("Invalid error code"); + error_code = MM_MOBILE_ERROR_UNKNOWN; + msg = "Unknown error"; + } + + return g_error_new_literal (MM_MOBILE_ERROR, error_code, msg); +} diff --git a/src/mm-errors.h b/src/mm-errors.h new file mode 100644 index 0000000..c02a351 --- /dev/null +++ b/src/mm-errors.h @@ -0,0 +1,131 @@ +/* -*- 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 Novell, Inc. + * Copyright (C) 2009 Red Hat, Inc. + */ + +#ifndef MM_MODEM_ERROR_H +#define MM_MODEM_ERROR_H + +#include <glib-object.h> + +enum { + MM_SERIAL_OPEN_FAILED = 0, + MM_SERIAL_SEND_FAILED = 1, + MM_SERIAL_RESPONSE_TIMEOUT = 2, + MM_SERIAL_OPEN_FAILED_NO_DEVICE = 3 +}; + +#define MM_SERIAL_ERROR (mm_serial_error_quark ()) +#define MM_TYPE_SERIAL_ERROR (mm_serial_error_get_type ()) + +GQuark mm_serial_error_quark (void); +GType mm_serial_error_get_type (void); + + +enum { + MM_MODEM_ERROR_GENERAL = 0, + MM_MODEM_ERROR_OPERATION_NOT_SUPPORTED = 1, + MM_MODEM_ERROR_CONNECTED = 2, + MM_MODEM_ERROR_DISCONNECTED = 3, + MM_MODEM_ERROR_OPERATION_IN_PROGRESS = 4, + MM_MODEM_ERROR_REMOVED = 5 +}; + +#define MM_MODEM_ERROR (mm_modem_error_quark ()) +#define MM_TYPE_MODEM_ERROR (mm_modem_error_get_type ()) + +GQuark mm_modem_error_quark (void); +GType mm_modem_error_get_type (void); + + +enum { + MM_MODEM_CONNECT_ERROR_NO_CARRIER = 3, + MM_MODEM_CONNECT_ERROR_NO_DIALTONE = 6, + MM_MODEM_CONNECT_ERROR_BUSY = 7, + MM_MODEM_CONNECT_ERROR_NO_ANSWER = 8, +}; + +#define MM_MODEM_CONNECT_ERROR (mm_modem_connect_error_quark ()) +#define MM_TYPE_MODEM_CONNECT_ERROR (mm_modem_connect_error_get_type ()) + +GQuark mm_modem_connect_error_quark (void); +GType mm_modem_connect_error_get_type (void); +GError *mm_modem_connect_error_for_code (int error_code); + + +enum { + MM_MOBILE_ERROR_PHONE_FAILURE = 0, + MM_MOBILE_ERROR_NO_CONNECTION = 1, + MM_MOBILE_ERROR_LINK_RESERVED = 2, + MM_MOBILE_ERROR_NOT_ALLOWED = 3, + MM_MOBILE_ERROR_NOT_SUPPORTED = 4, + MM_MOBILE_ERROR_PH_SIM_PIN = 5, + MM_MOBILE_ERROR_PH_FSIM_PIN = 6, + MM_MOBILE_ERROR_PH_FSIM_PUK = 7, + MM_MOBILE_ERROR_SIM_NOT_INSERTED = 10, + MM_MOBILE_ERROR_SIM_PIN = 11, + MM_MOBILE_ERROR_SIM_PUK = 12, + MM_MOBILE_ERROR_SIM_FAILURE = 13, + MM_MOBILE_ERROR_SIM_BUSY = 14, + MM_MOBILE_ERROR_SIM_WRONG = 15, + MM_MOBILE_ERROR_WRONG_PASSWORD = 16, + MM_MOBILE_ERROR_SIM_PIN2 = 17, + MM_MOBILE_ERROR_SIM_PUK2 = 18, + MM_MOBILE_ERROR_MEMORY_FULL = 20, + MM_MOBILE_ERROR_INVALID_INDEX = 21, + MM_MOBILE_ERROR_NOT_FOUND = 22, + MM_MOBILE_ERROR_MEMORY_FAILURE = 23, + MM_MOBILE_ERROR_TEXT_TOO_LONG = 24, + MM_MOBILE_ERROR_INVALID_CHARS = 25, + MM_MOBILE_ERROR_DIAL_STRING_TOO_LONG = 26, + MM_MOBILE_ERROR_DIAL_STRING_INVALID = 27, + MM_MOBILE_ERROR_NO_NETWORK = 30, + MM_MOBILE_ERROR_NETWORK_TIMEOUT = 31, + MM_MOBILE_ERROR_NETWORK_NOT_ALLOWED = 32, + MM_MOBILE_ERROR_NETWORK_PIN = 40, + MM_MOBILE_ERROR_NETWORK_PUK = 41, + MM_MOBILE_ERROR_NETWORK_SUBSET_PIN = 42, + MM_MOBILE_ERROR_NETWORK_SUBSET_PUK = 43, + MM_MOBILE_ERROR_SERVICE_PIN = 44, + MM_MOBILE_ERROR_SERVICE_PUK = 45, + MM_MOBILE_ERROR_CORP_PIN = 46, + MM_MOBILE_ERROR_CORP_PUK = 47, + MM_MOBILE_ERROR_HIDDEN_KEY = 48, + MM_MOBILE_ERROR_EAP_NOT_SUPPORTED = 49, + MM_MOBILE_ERROR_INCORRECT_PARAMS = 50, + MM_MOBILE_ERROR_UNKNOWN = 100, + + MM_MOBILE_ERROR_GPRS_ILLEGAL_MS = 103, + MM_MOBILE_ERROR_GPRS_ILLEGAL_ME = 106, + MM_MOBILE_ERROR_GPRS_SERVICE_NOT_ALLOWED = 107, + MM_MOBILE_ERROR_GPRS_PLMN_NOT_ALLOWED = 111, + MM_MOBILE_ERROR_GPRS_LOCATION_NOT_ALLOWED = 112, + MM_MOBILE_ERROR_GPRS_ROAMING_NOT_ALLOWED = 113, + MM_MOBILE_ERROR_GPRS_OPTION_NOT_SUPPORTED = 132, + MM_MOBILE_ERROR_GPRS_NOT_SUBSCRIBED = 133, + MM_MOBILE_ERROR_GPRS_OUT_OF_ORDER = 134, + MM_MOBILE_ERROR_GPRS_PDP_AUTH_FAILURE = 149, + MM_MOBILE_ERROR_GPRS_UNKNOWN = 148, + MM_MOBILE_ERROR_GPRS_INVALID_CLASS = 150 +}; + + +#define MM_MOBILE_ERROR (mm_mobile_error_quark ()) +#define MM_TYPE_MOBILE_ERROR (mm_mobile_error_get_type ()) + +GQuark mm_mobile_error_quark (void); +GType mm_mobile_error_get_type (void); +GError *mm_mobile_error_for_code (int error_code); + +#endif /* MM_MODEM_ERROR_H */ diff --git a/src/mm-generic-cdma.c b/src/mm-generic-cdma.c new file mode 100644 index 0000000..50cd86c --- /dev/null +++ b/src/mm-generic-cdma.c @@ -0,0 +1,1831 @@ +/* -*- 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 <stdio.h> +#include <ctype.h> +#include <errno.h> +#include <stdlib.h> + +#include "mm-generic-cdma.h" +#include "mm-modem-cdma.h" +#include "mm-modem-simple.h" +#include "mm-serial-port.h" +#include "mm-errors.h" +#include "mm-callback-info.h" +#include "mm-serial-parsers.h" + +static void simple_reg_callback (MMModemCdma *modem, + MMModemCdmaRegistrationState cdma_1x_reg_state, + MMModemCdmaRegistrationState evdo_reg_state, + GError *error, + gpointer user_data); + +static void simple_state_machine (MMModem *modem, GError *error, gpointer user_data); + +static void update_enabled_state (MMGenericCdma *self, + gboolean stay_connected, + MMModemStateReason reason); + +static void modem_init (MMModem *modem_class); +static void modem_cdma_init (MMModemCdma *cdma_class); +static void modem_simple_init (MMModemSimple *class); + +G_DEFINE_TYPE_EXTENDED (MMGenericCdma, mm_generic_cdma, MM_TYPE_MODEM_BASE, 0, + G_IMPLEMENT_INTERFACE (MM_TYPE_MODEM, modem_init) + G_IMPLEMENT_INTERFACE (MM_TYPE_MODEM_CDMA, modem_cdma_init) + G_IMPLEMENT_INTERFACE (MM_TYPE_MODEM_SIMPLE, modem_simple_init)) + +#define MM_GENERIC_CDMA_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), MM_TYPE_GENERIC_CDMA, MMGenericCdmaPrivate)) + +typedef struct { + guint32 cdma1x_quality; + guint32 evdo_quality; + gboolean valid; + gboolean evdo_rev0; + gboolean evdo_revA; + gboolean reg_try_css; + + MMModemCdmaRegistrationState cdma_1x_reg_state; + MMModemCdmaRegistrationState evdo_reg_state; + + guint reg_tries; + guint reg_retry_id; + guint reg_state_changed_id; + MMCallbackInfo *simple_connect_info; + + MMSerialPort *primary; + MMSerialPort *secondary; + MMPort *data; +} MMGenericCdmaPrivate; + +enum { + PROP_0, + PROP_EVDO_REV0, + PROP_EVDO_REVA, + PROP_REG_TRY_CSS, + LAST_PROP +}; + +MMModem * +mm_generic_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_GENERIC_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 +check_valid (MMGenericCdma *self) +{ + MMGenericCdmaPrivate *priv = MM_GENERIC_CDMA_GET_PRIVATE (self); + gboolean new_valid = FALSE; + + if (priv->primary && priv->data) + new_valid = TRUE; + + mm_modem_base_set_valid (MM_MODEM_BASE (self), new_valid); +} + +static gboolean +owns_port (MMModem *modem, const char *subsys, const char *name) +{ + return !!mm_modem_base_get_port (MM_MODEM_BASE (modem), subsys, name); +} + +MMPort * +mm_generic_cdma_grab_port (MMGenericCdma *self, + const char *subsys, + const char *name, + MMPortType suggested_type, + gpointer user_data, + GError **error) +{ + MMGenericCdmaPrivate *priv = MM_GENERIC_CDMA_GET_PRIVATE (self); + MMPortType ptype = MM_PORT_TYPE_IGNORED; + MMPort *port; + + g_return_val_if_fail (!strcmp (subsys, "net") || !strcmp (subsys, "tty"), FALSE); + if (priv->primary) + g_return_val_if_fail (suggested_type != MM_PORT_TYPE_PRIMARY, FALSE); + + if (!strcmp (subsys, "tty")) { + if (suggested_type != MM_PORT_TYPE_UNKNOWN) + ptype = suggested_type; + else { + if (!priv->primary) + ptype = MM_PORT_TYPE_PRIMARY; + else if (!priv->secondary) + ptype = MM_PORT_TYPE_SECONDARY; + } + } + + port = mm_modem_base_add_port (MM_MODEM_BASE (self), subsys, name, ptype); + if (port && MM_IS_SERIAL_PORT (port)) { + g_object_set (G_OBJECT (port), MM_PORT_CARRIER_DETECT, FALSE, NULL); + mm_serial_port_set_response_parser (MM_SERIAL_PORT (port), + mm_serial_parser_v1_parse, + mm_serial_parser_v1_new (), + mm_serial_parser_v1_destroy); + + if (ptype == MM_PORT_TYPE_PRIMARY) { + priv->primary = MM_SERIAL_PORT (port); + if (!priv->data) { + priv->data = port; + g_object_notify (G_OBJECT (self), MM_MODEM_DATA_DEVICE); + } + check_valid (self); + } else if (ptype == MM_PORT_TYPE_SECONDARY) + priv->secondary = MM_SERIAL_PORT (port); + } else { + /* Net device (if any) is the preferred data port */ + if (!priv->data || MM_IS_SERIAL_PORT (priv->data)) { + priv->data = port; + g_object_notify (G_OBJECT (self), MM_MODEM_DATA_DEVICE); + check_valid (self); + } + } + + return port; +} + +static gboolean +grab_port (MMModem *modem, + const char *subsys, + const char *name, + MMPortType suggested_type, + gpointer user_data, + GError **error) +{ + return !!mm_generic_cdma_grab_port (MM_GENERIC_CDMA (modem), subsys, name, suggested_type, user_data, error); +} + +static void +release_port (MMModem *modem, const char *subsys, const char *name) +{ + MMGenericCdmaPrivate *priv = MM_GENERIC_CDMA_GET_PRIVATE (modem); + MMPort *port; + + port = mm_modem_base_get_port (MM_MODEM_BASE (modem), subsys, name); + if (!port) + return; + + if (port == MM_PORT (priv->primary)) { + mm_modem_base_remove_port (MM_MODEM_BASE (modem), port); + priv->primary = NULL; + } + + if (port == priv->data) { + priv->data = NULL; + g_object_notify (G_OBJECT (modem), MM_MODEM_DATA_DEVICE); + } + + if (port == MM_PORT (priv->secondary)) { + mm_modem_base_remove_port (MM_MODEM_BASE (modem), port); + priv->secondary = NULL; + } + + check_valid (MM_GENERIC_CDMA (modem)); +} + +MMSerialPort * +mm_generic_cdma_get_port (MMGenericCdma *modem, + MMPortType ptype) +{ + g_return_val_if_fail (MM_IS_GENERIC_CDMA (modem), NULL); + g_return_val_if_fail (ptype != MM_PORT_TYPE_UNKNOWN, NULL); + + if (ptype == MM_PORT_TYPE_PRIMARY) + return MM_GENERIC_CDMA_GET_PRIVATE (modem)->primary; + else if (ptype == MM_PORT_TYPE_SECONDARY) + return MM_GENERIC_CDMA_GET_PRIVATE (modem)->secondary; + + return NULL; +} + +/*****************************************************************************/ + +void +mm_generic_cdma_set_1x_registration_state (MMGenericCdma *self, + MMModemCdmaRegistrationState new_state) +{ + MMGenericCdmaPrivate *priv; + + g_return_if_fail (self != NULL); + g_return_if_fail (MM_IS_GENERIC_CDMA (self)); + + priv = MM_GENERIC_CDMA_GET_PRIVATE (self); + + if (priv->cdma_1x_reg_state != new_state) { + priv->cdma_1x_reg_state = new_state; + + update_enabled_state (self, TRUE, MM_MODEM_STATE_REASON_NONE); + mm_modem_cdma_emit_registration_state_changed (MM_MODEM_CDMA (self), + priv->cdma_1x_reg_state, + priv->evdo_reg_state); + } +} + +void +mm_generic_cdma_set_evdo_registration_state (MMGenericCdma *self, + MMModemCdmaRegistrationState new_state) +{ + MMGenericCdmaPrivate *priv; + + g_return_if_fail (self != NULL); + g_return_if_fail (MM_IS_GENERIC_CDMA (self)); + + priv = MM_GENERIC_CDMA_GET_PRIVATE (self); + + if (priv->evdo_reg_state == new_state) + return; + + /* Don't update EVDO state if the card doesn't support it */ + if ( priv->evdo_rev0 + || priv->evdo_revA + || (new_state == MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN)) { + priv->evdo_reg_state = new_state; + + update_enabled_state (self, TRUE, MM_MODEM_STATE_REASON_NONE); + mm_modem_cdma_emit_registration_state_changed (MM_MODEM_CDMA (self), + priv->cdma_1x_reg_state, + priv->evdo_reg_state); + } +} + +MMModemCdmaRegistrationState +mm_generic_cdma_1x_get_registration_state_sync (MMGenericCdma *self) +{ + g_return_val_if_fail (self != NULL, MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN); + g_return_val_if_fail (MM_IS_GENERIC_CDMA (self), MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN); + + return MM_GENERIC_CDMA_GET_PRIVATE (self)->cdma_1x_reg_state; +} + +MMModemCdmaRegistrationState +mm_generic_cdma_evdo_get_registration_state_sync (MMGenericCdma *self) +{ + g_return_val_if_fail (self != NULL, MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN); + g_return_val_if_fail (MM_IS_GENERIC_CDMA (self), MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN); + + return MM_GENERIC_CDMA_GET_PRIVATE (self)->evdo_reg_state; +} + +/*****************************************************************************/ + +static void +update_enabled_state (MMGenericCdma *self, + gboolean stay_connected, + MMModemStateReason reason) +{ + MMGenericCdmaPrivate *priv = MM_GENERIC_CDMA_GET_PRIVATE (self); + + /* While connected we don't want registration status changes to change + * the modem's state away from CONNECTED. + */ + if (stay_connected && (mm_modem_get_state (MM_MODEM (self)) >= MM_MODEM_STATE_DISCONNECTING)) + return; + + if ( priv->cdma_1x_reg_state != MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN + || priv->evdo_reg_state != MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN) + mm_modem_set_state (MM_MODEM (self), MM_MODEM_STATE_REGISTERED, reason); + else + mm_modem_set_state (MM_MODEM (self), MM_MODEM_STATE_ENABLED, reason); +} + +static void +registration_cleanup (MMGenericCdma *self, GQuark error_class, guint32 error_num) +{ + MMGenericCdmaPrivate *priv = MM_GENERIC_CDMA_GET_PRIVATE (self); + GError *error = NULL; + + priv->reg_tries = 0; + + if (priv->reg_state_changed_id) { + g_signal_handler_disconnect (self, priv->reg_state_changed_id); + priv->reg_state_changed_id = 0; + } + + if (priv->reg_retry_id) { + g_source_remove (priv->reg_retry_id); + priv->reg_retry_id = 0; + } + + /* Return an error to any explicit callers of simple_connect */ + if (priv->simple_connect_info && error_class) { + error = g_error_new_literal (error_class, error_num, + "Connection attempt terminated"); + simple_state_machine (MM_MODEM (self), error, priv->simple_connect_info); + g_error_free (error); + } + priv->simple_connect_info = NULL; +} + +static void +enable_all_done (MMModem *modem, GError *error, gpointer user_data) +{ + MMCallbackInfo *info = user_data; + MMGenericCdma *self = MM_GENERIC_CDMA (info->modem); + MMGenericCdmaPrivate *priv = MM_GENERIC_CDMA_GET_PRIVATE (self); + + if (error) + info->error = g_error_copy (error); + else { + /* Open up the second port, if one exists */ + if (priv->secondary) { + if (!mm_serial_port_open (priv->secondary, &info->error)) { + g_assert (info->error); + goto out; + } + } + + update_enabled_state (self, FALSE, MM_MODEM_STATE_REASON_NONE); + } + +out: + if (info->error) { + mm_modem_set_state (MM_MODEM (info->modem), + MM_MODEM_STATE_DISABLED, + MM_MODEM_STATE_REASON_NONE); + } + + mm_callback_info_schedule (info); +} + +static void +enable_error_reporting_done (MMSerialPort *port, + GString *response, + GError *error, + gpointer user_data) +{ + MMCallbackInfo *info = (MMCallbackInfo *) user_data; + MMGenericCdma *self = MM_GENERIC_CDMA (info->modem); + + /* Just ignore errors, see comment in init_done() */ + if (error) + g_warning ("Your CDMA modem does not support +CMEE command"); + + if (MM_GENERIC_CDMA_GET_CLASS (self)->post_enable) + MM_GENERIC_CDMA_GET_CLASS (self)->post_enable (self, enable_all_done, info); + else + enable_all_done (MM_MODEM (self), NULL, info); +} + +static void +init_done (MMSerialPort *port, + GString *response, + GError *error, + gpointer user_data) +{ + MMCallbackInfo *info = (MMCallbackInfo *) user_data; + + if (error) { + mm_modem_set_state (MM_MODEM (info->modem), + MM_MODEM_STATE_DISABLED, + MM_MODEM_STATE_REASON_NONE); + + info->error = g_error_copy (error); + mm_callback_info_schedule (info); + } else { + /* Try to enable better error reporting. My experience so far indicates + there's some CDMA modems that does not support that. + FIXME: It's mandatory by spec, so it really shouldn't be optional. Figure + out which CDMA modems have problems with it and implement plugin for them. + */ + mm_serial_port_queue_command (port, "+CMEE=1", 3, enable_error_reporting_done, user_data); + } +} + +static void +flash_done (MMSerialPort *port, GError *error, gpointer user_data) +{ + MMCallbackInfo *info = (MMCallbackInfo *) user_data; + + if (error) { + mm_modem_set_state (MM_MODEM (info->modem), + MM_MODEM_STATE_DISABLED, + MM_MODEM_STATE_REASON_NONE); + + /* Flash failed for some reason */ + info->error = g_error_copy (error); + mm_callback_info_schedule (info); + return; + } + + mm_serial_port_queue_command (port, "Z E0 V1 X4 &C1", 3, init_done, user_data); +} + +static void +enable (MMModem *modem, + MMModemFn callback, + gpointer user_data) +{ + MMGenericCdma *self = MM_GENERIC_CDMA (modem); + MMGenericCdmaPrivate *priv = MM_GENERIC_CDMA_GET_PRIVATE (self); + MMCallbackInfo *info; + + info = mm_callback_info_new (modem, callback, user_data); + + if (!mm_serial_port_open (priv->primary, &info->error)) { + g_assert (info->error); + mm_callback_info_schedule (info); + return; + } + + mm_modem_set_state (MM_MODEM (info->modem), + MM_MODEM_STATE_ENABLING, + MM_MODEM_STATE_REASON_NONE); + + mm_serial_port_flash (priv->primary, 100, flash_done, info); +} + +static void +disable_set_previous_state (MMModem *modem, MMCallbackInfo *info) +{ + MMModemState prev_state; + + /* Reset old state since the operation failed */ + prev_state = GPOINTER_TO_UINT (mm_callback_info_get_data (info, MM_GENERIC_CDMA_PREV_STATE_TAG)); + mm_modem_set_state (modem, prev_state, MM_MODEM_STATE_REASON_NONE); +} + +static void +disable_all_done (MMModem *modem, GError *error, gpointer user_data) +{ + MMCallbackInfo *info = user_data; + + info->error = mm_modem_check_removed (modem, error); + if (info->error) { + if (modem) + disable_set_previous_state (modem, info); + } else { + MMGenericCdma *self = MM_GENERIC_CDMA (info->modem); + MMGenericCdmaPrivate *priv = MM_GENERIC_CDMA_GET_PRIVATE (self); + + mm_serial_port_close (priv->primary); + mm_modem_set_state (modem, MM_MODEM_STATE_DISABLED, MM_MODEM_STATE_REASON_NONE); + + priv->cdma_1x_reg_state = MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN; + priv->evdo_reg_state = MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN; + } + + mm_callback_info_schedule (info); +} + +static void +disable_flash_done (MMSerialPort *port, + GError *error, + gpointer user_data) +{ + MMCallbackInfo *info = user_data; + MMGenericCdma *self; + + info->error = mm_modem_check_removed (info->modem, error); + if (info->error) { + if (info->modem) + disable_set_previous_state (info->modem, info); + mm_callback_info_schedule (info); + return; + } + + self = MM_GENERIC_CDMA (info->modem); + + if (MM_GENERIC_CDMA_GET_CLASS (self)->post_disable) + MM_GENERIC_CDMA_GET_CLASS (self)->post_disable (self, disable_all_done, info); + else + disable_all_done (MM_MODEM (self), NULL, info); +} + +static void +disable (MMModem *modem, + MMModemFn callback, + gpointer user_data) +{ + MMGenericCdma *self = MM_GENERIC_CDMA (modem); + MMGenericCdmaPrivate *priv = MM_GENERIC_CDMA_GET_PRIVATE (self); + MMCallbackInfo *info; + MMModemState state; + + /* Tear down any ongoing registration */ + registration_cleanup (self, MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL); + + info = mm_callback_info_new (modem, callback, user_data); + + /* Cache the previous state so we can reset it if the operation fails */ + state = mm_modem_get_state (modem); + mm_callback_info_set_data (info, + MM_GENERIC_CDMA_PREV_STATE_TAG, + GUINT_TO_POINTER (state), + NULL); + + if (priv->secondary) + mm_serial_port_close (priv->secondary); + + mm_modem_set_state (MM_MODEM (info->modem), + MM_MODEM_STATE_DISABLING, + MM_MODEM_STATE_REASON_NONE); + + if (mm_port_get_connected (MM_PORT (priv->primary))) + mm_serial_port_flash (priv->primary, 1000, disable_flash_done, info); + else + disable_flash_done (priv->primary, NULL, info); +} + +static void +dial_done (MMSerialPort *port, + GString *response, + GError *error, + gpointer user_data) +{ + MMCallbackInfo *info = (MMCallbackInfo *) user_data; + + info->error = mm_modem_check_removed (info->modem, error); + if (info->error) { + if (info->modem) + update_enabled_state (MM_GENERIC_CDMA (info->modem), FALSE, MM_MODEM_STATE_REASON_NONE); + } else { + MMGenericCdma *self = MM_GENERIC_CDMA (info->modem); + MMGenericCdmaPrivate *priv = MM_GENERIC_CDMA_GET_PRIVATE (self); + + /* Clear reg tries; we're obviously registered by this point */ + registration_cleanup (self, 0, 0); + + mm_port_set_connected (priv->data, TRUE); + mm_modem_set_state (info->modem, MM_MODEM_STATE_CONNECTED, MM_MODEM_STATE_REASON_NONE); + } + + mm_callback_info_schedule (info); +} + +static void +connect (MMModem *modem, + const char *number, + MMModemFn callback, + gpointer user_data) +{ + MMGenericCdmaPrivate *priv = MM_GENERIC_CDMA_GET_PRIVATE (modem); + MMCallbackInfo *info; + char *command; + + mm_modem_set_state (modem, MM_MODEM_STATE_CONNECTING, MM_MODEM_STATE_REASON_NONE); + + info = mm_callback_info_new (modem, callback, user_data); + command = g_strconcat ("DT", number, NULL); + mm_serial_port_queue_command (priv->primary, command, 90, dial_done, info); + g_free (command); +} + +static void +disconnect_flash_done (MMSerialPort *port, + GError *error, + gpointer user_data) +{ + MMCallbackInfo *info = (MMCallbackInfo *) user_data; + MMModemState prev_state; + + info->error = mm_modem_check_removed (info->modem, error); + if (info->error) { + if (info->modem) { + /* Reset old state since the operation failed */ + prev_state = GPOINTER_TO_UINT (mm_callback_info_get_data (info, MM_GENERIC_CDMA_PREV_STATE_TAG)); + mm_modem_set_state (MM_MODEM (info->modem), + prev_state, + MM_MODEM_STATE_REASON_NONE); + } + } else { + mm_port_set_connected (MM_GENERIC_CDMA_GET_PRIVATE (info->modem)->data, FALSE); + update_enabled_state (MM_GENERIC_CDMA (info->modem), FALSE, MM_MODEM_STATE_REASON_NONE); + } + + mm_callback_info_schedule (info); +} + +static void +disconnect (MMModem *modem, + MMModemFn callback, + gpointer user_data) +{ + MMGenericCdmaPrivate *priv = MM_GENERIC_CDMA_GET_PRIVATE (modem); + MMCallbackInfo *info; + MMModemState state; + + g_return_if_fail (priv->primary != NULL); + + info = mm_callback_info_new (modem, callback, user_data); + + /* Cache the previous state so we can reset it if the operation fails */ + state = mm_modem_get_state (modem); + mm_callback_info_set_data (info, + MM_GENERIC_CDMA_PREV_STATE_TAG, + GUINT_TO_POINTER (state), + NULL); + + mm_modem_set_state (modem, MM_MODEM_STATE_DISCONNECTING, MM_MODEM_STATE_REASON_NONE); + mm_serial_port_flash (priv->primary, 1000, disconnect_flash_done, info); +} + +static void +card_info_invoke (MMCallbackInfo *info) +{ + MMModemInfoFn callback = (MMModemInfoFn) info->callback; + + callback (info->modem, + (char *) mm_callback_info_get_data (info, "card-info-manufacturer"), + (char *) mm_callback_info_get_data (info, "card-info-model"), + (char *) mm_callback_info_get_data (info, "card-info-version"), + info->error, info->user_data); +} + +static const char * +strip_response (const char *resp, const char *cmd) +{ + const char *p = resp; + + if (p) { + if (!strncmp (p, cmd, strlen (cmd))) + p += strlen (cmd); + while (*p == ' ') + p++; + } + return p; +} + +static void +get_version_done (MMSerialPort *port, + GString *response, + GError *error, + gpointer user_data) +{ + MMCallbackInfo *info = (MMCallbackInfo *) user_data; + const char *p; + + if (!error) { + p = strip_response (response->str, "+GMR:"); + mm_callback_info_set_data (info, "card-info-version", g_strdup (p), g_free); + } else if (!info->error) + info->error = g_error_copy (error); + + mm_callback_info_schedule (info); +} + +static void +get_model_done (MMSerialPort *port, + GString *response, + GError *error, + gpointer user_data) +{ + MMCallbackInfo *info = (MMCallbackInfo *) user_data; + const char *p; + + if (!error) { + p = strip_response (response->str, "+GMM:"); + mm_callback_info_set_data (info, "card-info-model", g_strdup (p), g_free); + } else if (!info->error) + info->error = g_error_copy (error); +} + +static void +get_manufacturer_done (MMSerialPort *port, + GString *response, + GError *error, + gpointer user_data) +{ + MMCallbackInfo *info = (MMCallbackInfo *) user_data; + const char *p; + + if (!error) { + p = strip_response (response->str, "+GMI:"); + mm_callback_info_set_data (info, "card-info-manufacturer", g_strdup (p), g_free); + } else + info->error = g_error_copy (error); +} + +static void +get_card_info (MMModem *modem, + MMModemInfoFn callback, + gpointer user_data) +{ + MMGenericCdmaPrivate *priv = MM_GENERIC_CDMA_GET_PRIVATE (modem); + MMCallbackInfo *info; + MMSerialPort *port = priv->primary; + + info = mm_callback_info_new_full (MM_MODEM (modem), + card_info_invoke, + G_CALLBACK (callback), + user_data); + + if (mm_port_get_connected (MM_PORT (priv->primary))) { + if (!priv->secondary) { + info->error = g_error_new_literal (MM_MODEM_ERROR, MM_MODEM_ERROR_CONNECTED, + "Cannot modem info while connected"); + mm_callback_info_schedule (info); + return; + } + + /* Use secondary port if primary is connected */ + port = priv->secondary; + } + + mm_serial_port_queue_command_cached (port, "+GMI", 3, get_manufacturer_done, info); + mm_serial_port_queue_command_cached (port, "+GMM", 3, get_model_done, info); + mm_serial_port_queue_command_cached (port, "+GMR", 3, get_version_done, info); +} + +/*****************************************************************************/ + +void +mm_generic_cdma_update_cdma1x_quality (MMGenericCdma *self, guint32 quality) +{ + MMGenericCdmaPrivate *priv; + + g_return_if_fail (MM_IS_GENERIC_CDMA (self)); + g_return_if_fail (quality >= 0 && quality <= 100); + + priv = MM_GENERIC_CDMA_GET_PRIVATE (self); + if (priv->cdma1x_quality != quality) { + priv->cdma1x_quality = quality; + mm_modem_cdma_emit_signal_quality_changed (MM_MODEM_CDMA (self), quality); + } +} + +void +mm_generic_cdma_update_evdo_quality (MMGenericCdma *self, guint32 quality) +{ + MMGenericCdmaPrivate *priv; + + g_return_if_fail (MM_IS_GENERIC_CDMA (self)); + g_return_if_fail (quality >= 0 && quality <= 100); + + priv = MM_GENERIC_CDMA_GET_PRIVATE (self); + if (priv->evdo_quality != quality) { + priv->evdo_quality = quality; + // FIXME: emit a signal + } +} + +#define CSQ2_TRIED "csq?-tried" + +static void +get_signal_quality_done (MMSerialPort *port, + GString *response, + GError *error, + gpointer user_data) +{ + MMGenericCdmaPrivate *priv; + MMCallbackInfo *info = (MMCallbackInfo *) user_data; + char *reply = response->str; + + if (error) { + if (mm_callback_info_get_data (info, CSQ2_TRIED)) + info->error = g_error_copy (error); + else { + /* Some modems want +CSQ, others want +CSQ?, and some of both types + * will return ERROR if they don't get the command they want. So + * try the other command if the first one fails. + */ + mm_callback_info_set_data (info, CSQ2_TRIED, GUINT_TO_POINTER (1), NULL); + mm_serial_port_queue_command (port, "+CSQ?", 3, get_signal_quality_done, info); + return; + } + } else { + int quality, ber; + + /* Got valid reply */ + if (!strncmp (reply, "+CSQ: ", 6)) + reply += 6; + + if (sscanf (reply, "%d, %d", &quality, &ber)) { + /* 99 means unknown/no service */ + if (quality == 99) { + info->error = g_error_new_literal (MM_MOBILE_ERROR, + MM_MOBILE_ERROR_NO_NETWORK, + "No service"); + } else { + /* Normalize the quality */ + quality = CLAMP (quality, 0, 31) * 100 / 31; + + priv = MM_GENERIC_CDMA_GET_PRIVATE (info->modem); + mm_callback_info_set_result (info, GUINT_TO_POINTER (quality), NULL); + if (priv->cdma1x_quality != quality) { + priv->cdma1x_quality = quality; + mm_modem_cdma_emit_signal_quality_changed (MM_MODEM_CDMA (info->modem), quality); + } + } + } 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) +{ + MMGenericCdmaPrivate *priv = MM_GENERIC_CDMA_GET_PRIVATE (modem); + MMCallbackInfo *info; + MMSerialPort *port = priv->primary; + + if (mm_port_get_connected (MM_PORT (priv->primary))) { + if (!priv->secondary) { + g_message ("Returning saved signal quality %d", priv->cdma1x_quality); + callback (MM_MODEM (modem), priv->cdma1x_quality, NULL, user_data); + return; + } + + /* Use secondary port if primary is connected */ + port = priv->secondary; + } + + info = mm_callback_info_uint_new (MM_MODEM (modem), callback, user_data); + mm_serial_port_queue_command (port, "+CSQ", 3, get_signal_quality_done, info); +} + +static void +get_string_done (MMSerialPort *port, + GString *response, + GError *error, + gpointer user_data) +{ + MMCallbackInfo *info = (MMCallbackInfo *) user_data; + const char *p; + + if (error) + info->error = g_error_copy (error); + else { + p = strip_response (response->str, "+GSN:"); + mm_callback_info_set_result (info, g_strdup (p), g_free); + } + + mm_callback_info_schedule (info); +} + +static void +get_esn (MMModemCdma *modem, + MMModemStringFn callback, + gpointer user_data) +{ + MMGenericCdmaPrivate *priv = MM_GENERIC_CDMA_GET_PRIVATE (modem); + MMCallbackInfo *info; + GError *error; + MMSerialPort *port = priv->primary; + + if (mm_port_get_connected (MM_PORT (priv->primary))) { + if (!priv->secondary) { + error = g_error_new_literal (MM_MODEM_ERROR, MM_MODEM_ERROR_CONNECTED, + "Cannot get ESN while connected"); + callback (MM_MODEM (modem), NULL, error, user_data); + g_error_free (error); + return; + } + + /* Use secondary port if primary is connected */ + port = priv->secondary; + } + + info = mm_callback_info_string_new (MM_MODEM (modem), callback, user_data); + mm_serial_port_queue_command_cached (port, "+GSN", 3, get_string_done, info); +} + +static void +serving_system_invoke (MMCallbackInfo *info) +{ + MMModemCdmaServingSystemFn callback = (MMModemCdmaServingSystemFn) info->callback; + + callback (MM_MODEM_CDMA (info->modem), + GPOINTER_TO_UINT (mm_callback_info_get_data (info, "class")), + (unsigned char) GPOINTER_TO_UINT (mm_callback_info_get_data (info, "band")), + GPOINTER_TO_UINT (mm_callback_info_get_data (info, "sid")), + info->error, + info->user_data); +} + +static int +normalize_class (const char *orig_class) +{ + char class; + + g_return_val_if_fail (orig_class != NULL, '0'); + + class = toupper (orig_class[0]); + + /* Cellular (850MHz) */ + if (class == '1' || class == 'C') + return 1; + /* PCS (1900MHz) */ + if (class == '2' || class == 'P') + return 2; + + /* Unknown/not registered */ + return 0; +} + +static char +normalize_band (const char *long_band, int *out_class) +{ + char band; + + g_return_val_if_fail (long_band != NULL, 'Z'); + + /* There are two response formats for the band; one includes the band + * class and the other doesn't. For modems that include the band class + * (ex Novatel S720) you'll see "Px" or "Cx" depending on whether the modem + * is registered on a PCS/1900 (P) or Cellular/850 (C) system. + */ + band = toupper (long_band[0]); + + /* Possible band class in first position; return it */ + if (band == 'C' || band == 'P') { + char tmp[2] = { band, '\0' }; + + *out_class = normalize_class (tmp); + band = toupper (long_band[1]); + } + + /* normalize to A - F, and Z */ + if (band >= 'A' && band <= 'F') + return band; + + /* Unknown/not registered */ + return 'Z'; +} + +static int +convert_sid (const char *sid) +{ + long int tmp_sid; + + g_return_val_if_fail (sid != NULL, 99999); + + errno = 0; + tmp_sid = strtol (sid, NULL, 10); + if ((errno == EINVAL) || (errno == ERANGE)) + return 99999; + else if (tmp_sid < G_MININT || tmp_sid > G_MAXINT) + return 99999; + + return (int) tmp_sid; +} + +static void +serving_system_done (MMSerialPort *port, + GString *response, + GError *error, + gpointer user_data) +{ + MMCallbackInfo *info = (MMCallbackInfo *) user_data; + char *reply = response->str; + int class = 0, sid = 99999, num; + unsigned char band = 'Z'; + gboolean success = FALSE; + + if (error) { + info->error = g_error_copy (error); + goto out; + } + + if (strstr (reply, "+CSS: ")) + reply += 6; + + num = sscanf (reply, "? , %d", &sid); + if (num == 1) { + /* UTStarcom and Huawei modems that use IS-707-A format; note that + * this format obviously doesn't have other indicators like band and + * class and thus SID 0 will be reported as "no service" (see below). + */ + class = 0; + band = 'Z'; + success = TRUE; + } else { + GRegex *r; + GMatchInfo *match_info; + int override_class = 0; + + /* Format is "<band_class>,<band>,<sid>" */ + r = g_regex_new ("\\s*([^,]*?)\\s*,\\s*([^,]*?)\\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 Serving System results (regex creation failed)."); + goto out; + } + + g_regex_match (r, reply, 0, &match_info); + if (g_match_info_get_match_count (match_info) >= 3) { + char *str; + + /* band class */ + str = g_match_info_fetch (match_info, 1); + class = normalize_class (str); + g_free (str); + + /* band */ + str = g_match_info_fetch (match_info, 2); + band = normalize_band (str, &override_class); + if (override_class) + class = override_class; + g_free (str); + + /* sid */ + str = g_match_info_fetch (match_info, 3); + sid = convert_sid (str); + g_free (str); + + success = TRUE; + } + + g_match_info_free (match_info); + g_regex_unref (r); + } + + if (success) { + gboolean class_ok = FALSE, band_ok = FALSE; + + /* Normalize the SID */ + if (sid < 0 || sid > 32767) + sid = 99999; + + if (class == 1 || class == 2) + class_ok = TRUE; + if (band != 'Z') + band_ok = TRUE; + + /* Return 'no service' if none of the elements of the +CSS response + * indicate that the modem has service. Note that this allows SID 0 + * when at least one of the other elements indicates service. + * Normally we'd treat SID 0 as 'no service' but some modems + * (Sierra 5725) sometimes return SID 0 even when registered. + */ + if (sid == 0 && !class_ok && !band_ok) + sid = 99999; + + /* 99999 means unknown/no service */ + if (sid == 99999) { + /* NOTE: update reg_state_css_response() if this error changes */ + info->error = g_error_new_literal (MM_MOBILE_ERROR, + MM_MOBILE_ERROR_NO_NETWORK, + "No service"); + } else { + mm_callback_info_set_data (info, "class", GUINT_TO_POINTER (class), NULL); + mm_callback_info_set_data (info, "band", GUINT_TO_POINTER ((guint32) band), NULL); + mm_callback_info_set_data (info, "sid", GUINT_TO_POINTER (sid), NULL); + } + } else { + info->error = g_error_new_literal (MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL, + "Could not parse Serving System results."); + } + + out: + mm_callback_info_schedule (info); +} + +static void +get_serving_system (MMModemCdma *modem, + MMModemCdmaServingSystemFn callback, + gpointer user_data) +{ + MMGenericCdmaPrivate *priv = MM_GENERIC_CDMA_GET_PRIVATE (modem); + MMCallbackInfo *info; + GError *error; + MMSerialPort *port = priv->primary; + + if (mm_port_get_connected (MM_PORT (priv->primary))) { + if (!priv->secondary) { + error = g_error_new_literal (MM_MODEM_ERROR, MM_MODEM_ERROR_CONNECTED, + "Cannot get serving system while connected"); + callback (modem, 0, 0, 0, error, user_data); + g_error_free (error); + return; + } + + /* Use secondary port if primary is connected */ + port = priv->secondary; + } + + info = mm_callback_info_new_full (MM_MODEM (modem), + serving_system_invoke, + G_CALLBACK (callback), + user_data); + + mm_serial_port_queue_command (port, "+CSS?", 3, serving_system_done, info); +} + +#define CDMA_1X_STATE_TAG "cdma-1x-reg-state" +#define EVDO_STATE_TAG "evdo-reg-state" + +void +mm_generic_cdma_query_reg_state_set_callback_1x_state (MMCallbackInfo *info, + MMModemCdmaRegistrationState new_state) +{ + g_return_if_fail (info != NULL); + g_return_if_fail (info->modem != NULL); + g_return_if_fail (MM_IS_GENERIC_CDMA (info->modem)); + + mm_callback_info_set_data (info, CDMA_1X_STATE_TAG, GUINT_TO_POINTER (new_state), NULL); +} + +void +mm_generic_cdma_query_reg_state_set_callback_evdo_state (MMCallbackInfo *info, + MMModemCdmaRegistrationState new_state) +{ + g_return_if_fail (info != NULL); + g_return_if_fail (info->modem != NULL); + g_return_if_fail (MM_IS_GENERIC_CDMA (info->modem)); + + mm_callback_info_set_data (info, EVDO_STATE_TAG, GUINT_TO_POINTER (new_state), NULL); +} + +static void +registration_state_invoke (MMCallbackInfo *info) +{ + MMModemCdmaRegistrationStateFn callback = (MMModemCdmaRegistrationStateFn) info->callback; + + /* note: This is the MMModemCdma interface callback */ + callback (MM_MODEM_CDMA (info->modem), + GPOINTER_TO_UINT (mm_callback_info_get_data (info, CDMA_1X_STATE_TAG)), + GPOINTER_TO_UINT (mm_callback_info_get_data (info, EVDO_STATE_TAG)), + info->error, + info->user_data); +} + +MMCallbackInfo * +mm_generic_cdma_query_reg_state_callback_info_new (MMGenericCdma *self, + MMModemCdmaRegistrationStateFn callback, + gpointer user_data) +{ + MMGenericCdmaPrivate *priv; + MMCallbackInfo *info; + + g_return_val_if_fail (self != NULL, NULL); + g_return_val_if_fail (MM_IS_GENERIC_CDMA (self), NULL); + g_return_val_if_fail (callback != NULL, NULL); + + info = mm_callback_info_new_full (MM_MODEM (self), + registration_state_invoke, + G_CALLBACK (callback), + user_data); + + /* Fill with current state */ + priv = MM_GENERIC_CDMA_GET_PRIVATE (self); + mm_callback_info_set_data (info, + CDMA_1X_STATE_TAG, + GUINT_TO_POINTER (priv->cdma_1x_reg_state), + NULL); + mm_callback_info_set_data (info, + EVDO_STATE_TAG, + GUINT_TO_POINTER (priv->evdo_reg_state), + NULL); + return info; +} + +static void +set_callback_1x_state_helper (MMCallbackInfo *info, + MMModemCdmaRegistrationState new_state) +{ + MMGenericCdma *self = MM_GENERIC_CDMA (info->modem); + MMGenericCdmaPrivate *priv = MM_GENERIC_CDMA_GET_PRIVATE (info->modem); + + mm_generic_cdma_set_1x_registration_state (self, new_state); + mm_generic_cdma_query_reg_state_set_callback_1x_state (info, priv->cdma_1x_reg_state); +} + +static void +set_callback_evdo_state_helper (MMCallbackInfo *info, + MMModemCdmaRegistrationState new_state) +{ + MMGenericCdma *self = MM_GENERIC_CDMA (info->modem); + MMGenericCdmaPrivate *priv = MM_GENERIC_CDMA_GET_PRIVATE (info->modem); + + mm_generic_cdma_set_evdo_registration_state (self, new_state); + mm_generic_cdma_query_reg_state_set_callback_evdo_state (info, priv->evdo_reg_state); +} + +static void +reg_state_query_done (MMModemCdma *cdma, + MMModemCdmaRegistrationState cdma_1x_reg_state, + MMModemCdmaRegistrationState evdo_reg_state, + GError *error, + gpointer user_data) +{ + MMCallbackInfo *info = (MMCallbackInfo *) user_data; + + if (error) + info->error = g_error_copy (error); + else { + set_callback_1x_state_helper (info, cdma_1x_reg_state); + set_callback_evdo_state_helper (info, evdo_reg_state); + } + + mm_callback_info_schedule (info); +} + +static void +query_subclass_registration_state (MMGenericCdma *self, MMCallbackInfo *info) +{ + /* Let subclasses figure out roaming and detailed registration state */ + if (MM_GENERIC_CDMA_GET_CLASS (self)->query_registration_state) { + MM_GENERIC_CDMA_GET_CLASS (self)->query_registration_state (self, + reg_state_query_done, + info); + } else { + /* Or if the subclass doesn't implement more specific checking, + * assume we're registered. + */ + reg_state_query_done (MM_MODEM_CDMA (self), + MM_MODEM_CDMA_REGISTRATION_STATE_REGISTERED, + MM_MODEM_CDMA_REGISTRATION_STATE_REGISTERED, + NULL, + info); + } +} + +static void +reg_state_css_response (MMModemCdma *cdma, + guint32 class, + unsigned char band, + guint32 sid, + GError *error, + gpointer user_data) +{ + MMCallbackInfo *info = (MMCallbackInfo *) user_data; + + /* We'll get an error if the SID isn't valid, so detect that and + * report unknown registration state. + */ + if (error) { + if ( (error->domain == MM_MOBILE_ERROR) + && (error->code == MM_MOBILE_ERROR_NO_NETWORK)) { + set_callback_1x_state_helper (info, MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN); + set_callback_evdo_state_helper (info, MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN); + } else { + /* Some other error parsing CSS results */ + info->error = g_error_copy (error); + } + mm_callback_info_schedule (info); + return; + } + + query_subclass_registration_state (MM_GENERIC_CDMA (info->modem), info); +} + +static void +get_analog_digital_done (MMSerialPort *port, + GString *response, + GError *error, + gpointer user_data) +{ + MMCallbackInfo *info = (MMCallbackInfo *) user_data; + const char *reply; + long int int_cad; + + if (error) { + info->error = g_error_copy (error); + goto error; + } + + /* Strip any leading command tag and spaces */ + reply = strip_response (response->str, "+CAD:"); + + errno = 0; + int_cad = strtol (reply, NULL, 10); + if ((errno == EINVAL) || (errno == ERANGE)) { + info->error = g_error_new_literal (MM_MODEM_ERROR, + MM_MODEM_ERROR_GENERAL, + "Failed to parse +CAD response"); + goto error; + } + + if (int_cad == 1) { /* 1 == CDMA service */ + MMGenericCdmaPrivate *priv = MM_GENERIC_CDMA_GET_PRIVATE (info->modem); + + /* Now that we have some sort of service, check if the the device is + * registered on the network. + */ + + /* Some devices key the AT+CSS? response off the 1X state, but if the + * device has EVDO service but no 1X service, then reading AT+CSS? will + * error out too early. Let subclasses that know that their AT+CSS? + * response is wrong in this case handle more specific registration + * themselves; if they do, they'll set priv->reg_try_css to FALSE. + */ + if (priv->reg_try_css) { + get_serving_system (MM_MODEM_CDMA (info->modem), + reg_state_css_response, + info); + } else { + /* Subclass knows that AT+CSS? will respond incorrectly to EVDO + * state, so skip AT+CSS? query. + */ + query_subclass_registration_state (MM_GENERIC_CDMA (info->modem), info); + } + return; + } else { + /* No service */ + set_callback_1x_state_helper (info, MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN); + set_callback_evdo_state_helper (info, MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN); + } + +error: + mm_callback_info_schedule (info); +} + +static void +get_registration_state (MMModemCdma *modem, + MMModemCdmaRegistrationStateFn callback, + gpointer user_data) +{ + MMGenericCdmaPrivate *priv = MM_GENERIC_CDMA_GET_PRIVATE (modem); + MMCallbackInfo *info; + MMSerialPort *port = priv->primary; + + if (mm_port_get_connected (MM_PORT (priv->primary))) { + if (!priv->secondary) { + g_message ("Returning saved registration states: 1x: %d EVDO: %d", + priv->cdma_1x_reg_state, priv->evdo_reg_state); + callback (MM_MODEM_CDMA (modem), priv->cdma_1x_reg_state, priv->evdo_reg_state, NULL, user_data); + return; + } + + /* Use secondary port if primary is connected */ + port = priv->secondary; + } + + info = mm_generic_cdma_query_reg_state_callback_info_new (MM_GENERIC_CDMA (modem), callback, user_data); + mm_serial_port_queue_command (port, "+CAD?", 3, get_analog_digital_done, info); +} + +/*****************************************************************************/ +/* MMModemSimple interface */ + +typedef enum { + SIMPLE_STATE_BEGIN = 0, + SIMPLE_STATE_ENABLE, + SIMPLE_STATE_REGISTER, + SIMPLE_STATE_CONNECT, + SIMPLE_STATE_DONE +} SimpleState; + +static const char * +simple_get_string_property (MMCallbackInfo *info, 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); + 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 gboolean +simple_reg_retry (gpointer user_data) +{ + MMCallbackInfo *info = (MMCallbackInfo *) user_data; + + mm_modem_cdma_get_registration_state (MM_MODEM_CDMA (info->modem), + simple_reg_callback, + info); + return TRUE; +} + +static void +simple_reg_callback (MMModemCdma *modem, + MMModemCdmaRegistrationState cdma_1x_reg_state, + MMModemCdmaRegistrationState evdo_reg_state, + GError *error, + gpointer user_data) +{ + MMCallbackInfo *info = (MMCallbackInfo *) user_data; + MMGenericCdmaPrivate *priv = MM_GENERIC_CDMA_GET_PRIVATE (modem); + gboolean no_service_error = FALSE; + + if ( error + && (error->domain == MM_MOBILE_ERROR) + && (error->code == MM_MOBILE_ERROR_NO_NETWORK)) + no_service_error = TRUE; + + /* Fail immediately on anything but "no service" */ + if (error && !no_service_error) { + simple_state_machine (MM_MODEM (modem), error, info); + return; + } + + if ( no_service_error + || ( (cdma_1x_reg_state == MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN) + && (evdo_reg_state == MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN))) { + /* Not registered yet, queue up a retry */ + priv->reg_tries++; + if (priv->reg_tries > 15) { + error = g_error_new_literal (MM_MOBILE_ERROR, + MM_MOBILE_ERROR_NO_NETWORK, + "No service"); + simple_state_machine (MM_MODEM (modem), error, info); + g_error_free (error); + return; + } + + /* otherwise, just try again in a bit */ + if (!priv->reg_retry_id) + priv->reg_retry_id = g_timeout_add_seconds (4, simple_reg_retry, info); + } else { + /* Yay, at least one of 1x or EVDO is registered, we can proceed to dial */ + simple_state_machine (MM_MODEM (modem), NULL, info); + } +} + +static void +reg_state_changed (MMModemCdma *self, + MMModemCdmaRegistrationState cdma_1x_new_state, + MMModemCdmaRegistrationState evdo_new_state, + gpointer user_data) +{ +/* Disabled for now... changing the registration state from the + * subclass' query_registration_state handler also emits the registration + * state changed signal, which will call this function, and execute + * simple_state_machine() to advance to the next state. Then however + * query_registration_state will call its callback, which ends up in + * simple_reg_callback(), which calls simple_state_machine() too in + * the same mainloop iteration. Not good. So until that's sorted out + * we'll just have to poll registration state (every 4 seconds so its + * not that bad. + */ +#if 0 + MMCallbackInfo *info = user_data; + + /* If we're registered, we can proceed */ + if ( (cdma_1x_reg_state != MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN) + || (evdo_reg_state != MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN)) + simple_state_machine (MM_MODEM (modem), NULL, info); +#endif +} + +static SimpleState +set_simple_state (MMCallbackInfo *info, SimpleState state) +{ + mm_callback_info_set_data (info, "simple-connect-state", GUINT_TO_POINTER (state), NULL); + return state; +} + +static void +simple_state_machine (MMModem *modem, GError *error, gpointer user_data) +{ + MMCallbackInfo *info = (MMCallbackInfo *) user_data; + MMGenericCdmaPrivate *priv = MM_GENERIC_CDMA_GET_PRIVATE (info->modem); + SimpleState state = GPOINTER_TO_UINT (mm_callback_info_get_data (info, "simple-connect-state")); + const char *str; + guint id; + + if (error) { + info->error = g_error_copy (error); + goto out; + } + + switch (state) { + case SIMPLE_STATE_BEGIN: + state = set_simple_state (info, SIMPLE_STATE_ENABLE); + mm_modem_enable (modem, simple_state_machine, info); + break; + case SIMPLE_STATE_ENABLE: + state = set_simple_state (info, SIMPLE_STATE_REGISTER); + mm_modem_cdma_get_registration_state (MM_MODEM_CDMA (modem), + simple_reg_callback, + info); + id = g_signal_connect (modem, + MM_MODEM_CDMA_REGISTRATION_STATE_CHANGED, + G_CALLBACK (reg_state_changed), + info); + priv->reg_state_changed_id = id; + break; + case SIMPLE_STATE_REGISTER: + registration_cleanup (MM_GENERIC_CDMA (modem), 0, 0); + state = set_simple_state (info, SIMPLE_STATE_CONNECT); + mm_modem_set_state (modem, MM_MODEM_STATE_REGISTERED, MM_MODEM_STATE_REASON_NONE); + + str = simple_get_string_property (info, "number", &info->error); + mm_modem_connect (modem, str, simple_state_machine, info); + break; + case SIMPLE_STATE_CONNECT: + state = set_simple_state (info, SIMPLE_STATE_DONE); + break; + case SIMPLE_STATE_DONE: + break; + } + + out: + if (info->error || state == SIMPLE_STATE_DONE) { + registration_cleanup (MM_GENERIC_CDMA (modem), 0, 0); + mm_callback_info_schedule (info); + } +} + +static void +simple_connect (MMModemSimple *simple, + GHashTable *properties, + MMModemFn callback, + gpointer user_data) +{ + MMGenericCdma *self = MM_GENERIC_CDMA (simple); + MMGenericCdmaPrivate *priv = MM_GENERIC_CDMA_GET_PRIVATE (self); + MMCallbackInfo *info; + GError *error = NULL; + + if (priv->simple_connect_info) { + error = g_error_new_literal (MM_MODEM_ERROR, + MM_MODEM_ERROR_OPERATION_IN_PROGRESS, + "Connection is already in progress"); + callback (MM_MODEM (simple), error, user_data); + g_error_free (error); + return; + } + + info = mm_callback_info_new (MM_MODEM (simple), callback, user_data); + priv->simple_connect_info = info; + mm_callback_info_set_data (info, "simple-connect-properties", + g_hash_table_ref (properties), + (GDestroyNotify) g_hash_table_unref); + + /* At least number must be present */ + if (!simple_get_string_property (info, "number", &error)) { + if (!error) + error = g_error_new_literal (MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL, "Missing number property"); + } + + simple_state_machine (MM_MODEM (simple), error, info); +} + +static void +simple_free_gvalue (gpointer data) +{ + g_value_unset ((GValue *) data); + g_slice_free (GValue, data); +} + +static GValue * +simple_uint_value (guint32 i) +{ + GValue *val; + + val = g_slice_new0 (GValue); + g_value_init (val, G_TYPE_UINT); + g_value_set_uint (val, i); + + return val; +} + +static void +simple_status_got_signal_quality (MMModem *modem, + guint32 result, + GError *error, + gpointer user_data) +{ + if (error) + g_warning ("Error getting signal quality: %s", error->message); + else + g_hash_table_insert ((GHashTable *) user_data, "signal_quality", simple_uint_value (result)); +} + +static void +simple_get_status_invoke (MMCallbackInfo *info) +{ + MMModemSimpleGetStatusFn callback = (MMModemSimpleGetStatusFn) info->callback; + + callback (MM_MODEM_SIMPLE (info->modem), + (GHashTable *) mm_callback_info_get_data (info, "simple-get-status"), + info->error, info->user_data); +} + +static void +simple_get_status (MMModemSimple *simple, + MMModemSimpleGetStatusFn callback, + gpointer user_data) +{ + GHashTable *properties; + MMCallbackInfo *info; + + info = mm_callback_info_new_full (MM_MODEM (simple), + simple_get_status_invoke, + G_CALLBACK (callback), + user_data); + + properties = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, simple_free_gvalue); + mm_callback_info_set_data (info, "simple-get-status", properties, (GDestroyNotify) g_hash_table_unref); + mm_modem_cdma_get_signal_quality (MM_MODEM_CDMA (simple), simple_status_got_signal_quality, properties); +} + +/*****************************************************************************/ + +static void +modem_valid_changed (MMGenericCdma *self, GParamSpec *pspec, gpointer user_data) +{ + /* Be paranoid about tearing down any pending registration */ + if (!mm_modem_get_valid (MM_MODEM (self))) + registration_cleanup (self, MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL); +} + +/*****************************************************************************/ + +static void +modem_init (MMModem *modem_class) +{ + modem_class->owns_port = owns_port; + modem_class->grab_port = grab_port; + modem_class->release_port = release_port; + modem_class->enable = enable; + modem_class->disable = disable; + modem_class->connect = connect; + modem_class->disconnect = disconnect; + modem_class->get_info = get_card_info; +} + +static void +modem_cdma_init (MMModemCdma *cdma_class) +{ + cdma_class->get_signal_quality = get_signal_quality; + cdma_class->get_esn = get_esn; + cdma_class->get_serving_system = get_serving_system; + cdma_class->get_registration_state = get_registration_state; +} + +static void +modem_simple_init (MMModemSimple *class) +{ + class->connect = simple_connect; + class->get_status = simple_get_status; +} + +static GObject* +constructor (GType type, + guint n_construct_params, + GObjectConstructParam *construct_params) +{ + GObject *object; + + object = G_OBJECT_CLASS (mm_generic_cdma_parent_class)->constructor (type, + n_construct_params, + construct_params); + if (object) { + g_signal_connect (object, "notify::" MM_MODEM_VALID, + G_CALLBACK (modem_valid_changed), NULL); + } + + return object; +} + +static void +mm_generic_cdma_init (MMGenericCdma *self) +{ +} + +static void +set_property (GObject *object, guint prop_id, + const GValue *value, GParamSpec *pspec) +{ + MMGenericCdmaPrivate *priv = MM_GENERIC_CDMA_GET_PRIVATE (object); + + switch (prop_id) { + case MM_MODEM_PROP_TYPE: + break; + case PROP_EVDO_REV0: + priv->evdo_rev0 = g_value_get_boolean (value); + break; + case PROP_EVDO_REVA: + priv->evdo_revA = g_value_get_boolean (value); + break; + case PROP_REG_TRY_CSS: + priv->reg_try_css = g_value_get_boolean (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +get_property (GObject *object, guint prop_id, + GValue *value, GParamSpec *pspec) +{ + MMGenericCdmaPrivate *priv = MM_GENERIC_CDMA_GET_PRIVATE (object); + + switch (prop_id) { + case MM_MODEM_PROP_DATA_DEVICE: + if (priv->data) + g_value_set_string (value, mm_port_get_device (priv->data)); + else + g_value_set_string (value, NULL); + break; + case MM_MODEM_PROP_TYPE: + g_value_set_uint (value, MM_MODEM_TYPE_CDMA); + break; + case PROP_EVDO_REV0: + g_value_set_boolean (value, priv->evdo_rev0); + break; + case PROP_EVDO_REVA: + g_value_set_boolean (value, priv->evdo_revA); + break; + case PROP_REG_TRY_CSS: + g_value_set_boolean (value, priv->reg_try_css); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +dispose (GObject *object) +{ + registration_cleanup (MM_GENERIC_CDMA (object), MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL); + + G_OBJECT_CLASS (mm_generic_cdma_parent_class)->dispose (object); +} + +static void +finalize (GObject *object) +{ + G_OBJECT_CLASS (mm_generic_cdma_parent_class)->finalize (object); +} + +static void +mm_generic_cdma_class_init (MMGenericCdmaClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + mm_generic_cdma_parent_class = g_type_class_peek_parent (klass); + g_type_class_add_private (object_class, sizeof (MMGenericCdmaPrivate)); + + /* Virtual methods */ + object_class->set_property = set_property; + object_class->get_property = get_property; + object_class->dispose = dispose; + object_class->finalize = finalize; + object_class->constructor = constructor; + + /* Properties */ + g_object_class_override_property (object_class, + MM_MODEM_PROP_DATA_DEVICE, + MM_MODEM_DATA_DEVICE); + + g_object_class_override_property (object_class, + MM_MODEM_PROP_TYPE, + MM_MODEM_TYPE); + + g_object_class_install_property (object_class, PROP_EVDO_REV0, + g_param_spec_boolean (MM_GENERIC_CDMA_EVDO_REV0, + "EVDO rev0", + "Supports EVDO rev0", + FALSE, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); + + g_object_class_install_property (object_class, PROP_EVDO_REVA, + g_param_spec_boolean (MM_GENERIC_CDMA_EVDO_REVA, + "EVDO revA", + "Supports EVDO revA", + FALSE, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); + + g_object_class_install_property (object_class, PROP_REG_TRY_CSS, + g_param_spec_boolean (MM_GENERIC_CDMA_REGISTRATION_TRY_CSS, + "RegistrationTryCss", + "Use Serving System response when checking modem" + " registration state.", + TRUE, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); +} + diff --git a/src/mm-generic-cdma.h b/src/mm-generic-cdma.h new file mode 100644 index 0000000..5b4a0b6 --- /dev/null +++ b/src/mm-generic-cdma.h @@ -0,0 +1,111 @@ +/* -*- 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 Novell, Inc. + * Copyright (C) 2009 Red Hat, Inc. + */ + +#ifndef MM_GENERIC_CDMA_H +#define MM_GENERIC_CDMA_H + +#include "mm-modem.h" +#include "mm-modem-base.h" +#include "mm-modem-cdma.h" +#include "mm-serial-port.h" +#include "mm-callback-info.h" + +#define MM_TYPE_GENERIC_CDMA (mm_generic_cdma_get_type ()) +#define MM_GENERIC_CDMA(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_GENERIC_CDMA, MMGenericCdma)) +#define MM_GENERIC_CDMA_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_GENERIC_CDMA, MMGenericCdmaClass)) +#define MM_IS_GENERIC_CDMA(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_GENERIC_CDMA)) +#define MM_IS_GENERIC_CDMA_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_GENERIC_CDMA)) +#define MM_GENERIC_CDMA_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_GENERIC_CDMA, MMGenericCdmaClass)) + +#define MM_GENERIC_CDMA_EVDO_REV0 "evdo-rev0" +#define MM_GENERIC_CDMA_EVDO_REVA "evdo-revA" + +#define MM_GENERIC_CDMA_REGISTRATION_TRY_CSS "registration-try-css" + +typedef struct { + MMModemBase parent; +} MMGenericCdma; + +typedef struct { + MMModemBaseClass parent; + + void (*query_registration_state) (MMGenericCdma *self, + MMModemCdmaRegistrationStateFn callback, + gpointer user_data); + + /* Called after generic enable operations, but before the modem has entered + * the ENABLED state. + */ + void (*post_enable) (MMGenericCdma *self, + MMModemFn callback, + gpointer user_data); + + /* Called after generic disable operations, but before the modem has entered + * the DISABLED state. + */ + void (*post_disable) (MMGenericCdma *self, + MMModemFn callback, + gpointer user_data); +} MMGenericCdmaClass; + +GType mm_generic_cdma_get_type (void); + +MMModem *mm_generic_cdma_new (const char *device, + const char *driver, + const char *plugin, + gboolean evdo_rev0, + gboolean evdo_revA); + +/* Private, for subclasses */ + +#define MM_GENERIC_CDMA_PREV_STATE_TAG "prev-state" + +MMPort * mm_generic_cdma_grab_port (MMGenericCdma *self, + const char *subsys, + const char *name, + MMPortType suggested_type, + gpointer user_data, + GError **error); + +MMSerialPort *mm_generic_cdma_get_port (MMGenericCdma *modem, MMPortType ptype); + +void mm_generic_cdma_update_cdma1x_quality (MMGenericCdma *self, guint32 quality); +void mm_generic_cdma_update_evdo_quality (MMGenericCdma *self, guint32 quality); + +/* For unsolicited 1x registration state changes */ +void mm_generic_cdma_set_1x_registration_state (MMGenericCdma *self, + MMModemCdmaRegistrationState new_state); + +/* For unsolicited EVDO registration state changes */ +void mm_generic_cdma_set_evdo_registration_state (MMGenericCdma *self, + MMModemCdmaRegistrationState new_state); + +MMModemCdmaRegistrationState mm_generic_cdma_1x_get_registration_state_sync (MMGenericCdma *self); + +MMModemCdmaRegistrationState mm_generic_cdma_evdo_get_registration_state_sync (MMGenericCdma *self); + +/* query_registration_state class function helpers */ +MMCallbackInfo *mm_generic_cdma_query_reg_state_callback_info_new (MMGenericCdma *self, + MMModemCdmaRegistrationStateFn callback, + gpointer user_data); + +void mm_generic_cdma_query_reg_state_set_callback_1x_state (MMCallbackInfo *info, + MMModemCdmaRegistrationState new_state); + +void mm_generic_cdma_query_reg_state_set_callback_evdo_state (MMCallbackInfo *info, + MMModemCdmaRegistrationState new_state); + +#endif /* MM_GENERIC_CDMA_H */ diff --git a/src/mm-generic-gsm.c b/src/mm-generic-gsm.c new file mode 100644 index 0000000..4954ca1 --- /dev/null +++ b/src/mm-generic-gsm.c @@ -0,0 +1,2220 @@ +/* -*- 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. + * Copyright (C) 2009 Ericsson + */ + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <ctype.h> +#include "mm-generic-gsm.h" +#include "mm-modem-gsm-card.h" +#include "mm-modem-gsm-network.h" +#include "mm-modem-gsm-sms.h" +#include "mm-modem-simple.h" +#include "mm-errors.h" +#include "mm-callback-info.h" +#include "mm-serial-parsers.h" +#include "mm-modem-helpers.h" + +static void modem_init (MMModem *modem_class); +static void modem_gsm_card_init (MMModemGsmCard *gsm_card_class); +static void modem_gsm_network_init (MMModemGsmNetwork *gsm_network_class); +static void modem_gsm_sms_init (MMModemGsmSms *gsm_sms_class); +static void modem_simple_init (MMModemSimple *class); + +G_DEFINE_TYPE_EXTENDED (MMGenericGsm, mm_generic_gsm, MM_TYPE_MODEM_BASE, 0, + G_IMPLEMENT_INTERFACE (MM_TYPE_MODEM, modem_init) + G_IMPLEMENT_INTERFACE (MM_TYPE_MODEM_GSM_CARD, modem_gsm_card_init) + G_IMPLEMENT_INTERFACE (MM_TYPE_MODEM_GSM_NETWORK, modem_gsm_network_init) + G_IMPLEMENT_INTERFACE (MM_TYPE_MODEM_GSM_SMS, modem_gsm_sms_init) + G_IMPLEMENT_INTERFACE (MM_TYPE_MODEM_SIMPLE, modem_simple_init)) + +#define MM_GENERIC_GSM_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), MM_TYPE_GENERIC_GSM, MMGenericGsmPrivate)) + +typedef struct { + char *driver; + char *plugin; + char *device; + + gboolean valid; + + char *oper_code; + char *oper_name; + guint32 ip_method; + gboolean unsolicited_registration; + + MMModemGsmNetworkRegStatus reg_status; + guint pending_reg_id; + MMCallbackInfo *pending_reg_info; + + guint32 signal_quality; + guint32 cid; + + MMSerialPort *primary; + MMSerialPort *secondary; + MMPort *data; +} MMGenericGsmPrivate; + +static void get_registration_status (MMSerialPort *port, MMCallbackInfo *info); +static void read_operator_code_done (MMSerialPort *port, + GString *response, + GError *error, + gpointer user_data); + +static void read_operator_name_done (MMSerialPort *port, + GString *response, + GError *error, + gpointer user_data); + +static void reg_state_changed (MMSerialPort *port, + GMatchInfo *match_info, + gpointer user_data); + +MMModem * +mm_generic_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_GENERIC_GSM, + MM_MODEM_MASTER_DEVICE, device, + MM_MODEM_DRIVER, driver, + MM_MODEM_PLUGIN, plugin, + NULL)); +} + +void +mm_generic_gsm_set_unsolicited_registration (MMGenericGsm *modem, + gboolean enabled) +{ + g_return_if_fail (MM_IS_GENERIC_GSM (modem)); + + MM_GENERIC_GSM_GET_PRIVATE (modem)->unsolicited_registration = enabled; +} + +void +mm_generic_gsm_set_cid (MMGenericGsm *modem, guint32 cid) +{ + g_return_if_fail (MM_IS_GENERIC_GSM (modem)); + + MM_GENERIC_GSM_GET_PRIVATE (modem)->cid = cid; +} + +guint32 +mm_generic_gsm_get_cid (MMGenericGsm *modem) +{ + g_return_val_if_fail (MM_IS_GENERIC_GSM (modem), 0); + + return MM_GENERIC_GSM_GET_PRIVATE (modem)->cid; +} + +static void +got_signal_quality (MMModem *modem, + guint32 result, + GError *error, + gpointer user_data) +{ +} + +void +mm_generic_gsm_set_reg_status (MMGenericGsm *modem, + MMModemGsmNetworkRegStatus status) +{ + MMGenericGsmPrivate *priv; + + g_return_if_fail (MM_IS_GENERIC_GSM (modem)); + + priv = MM_GENERIC_GSM_GET_PRIVATE (modem); + + if (priv->reg_status != status) { + priv->reg_status = status; + + g_debug ("Registration state changed: %d", status); + + if (status == MM_MODEM_GSM_NETWORK_REG_STATUS_HOME || + status == MM_MODEM_GSM_NETWORK_REG_STATUS_ROAMING) { + mm_serial_port_queue_command (priv->primary, "+COPS=3,2;+COPS?", 3, read_operator_code_done, modem); + mm_serial_port_queue_command (priv->primary, "+COPS=3,0;+COPS?", 3, read_operator_name_done, modem); + mm_modem_gsm_network_get_signal_quality (MM_MODEM_GSM_NETWORK (modem), got_signal_quality, NULL); + } else { + g_free (priv->oper_code); + g_free (priv->oper_name); + priv->oper_code = priv->oper_name = NULL; + + mm_modem_gsm_network_registration_info (MM_MODEM_GSM_NETWORK (modem), priv->reg_status, + priv->oper_code, priv->oper_name); + } + + mm_generic_gsm_update_enabled_state (modem, TRUE, MM_MODEM_STATE_REASON_NONE); + } +} + +typedef struct { + const char *result; + guint code; +} CPinResult; + +static void +pin_check_done (MMSerialPort *port, + GString *response, + GError *error, + gpointer user_data) +{ + MMCallbackInfo *info = (MMCallbackInfo *) user_data; + gboolean parsed = FALSE; + static CPinResult results[] = { + { "SIM PIN", MM_MOBILE_ERROR_SIM_PIN }, + { "SIM PUK", MM_MOBILE_ERROR_SIM_PUK }, + { "PH-SIM PIN", MM_MOBILE_ERROR_PH_SIM_PIN }, + { "PH-FSIM PIN", MM_MOBILE_ERROR_PH_FSIM_PIN }, + { "PH-FSIM PUK", MM_MOBILE_ERROR_PH_FSIM_PUK }, + { "SIM PIN2", MM_MOBILE_ERROR_SIM_PIN2 }, + { "SIM PUK2", MM_MOBILE_ERROR_SIM_PUK2 }, + { "PH-NET PIN", MM_MOBILE_ERROR_NETWORK_PIN }, + { "PH-NET PUK", MM_MOBILE_ERROR_NETWORK_PUK }, + { "PH-NETSUB PIN", MM_MOBILE_ERROR_NETWORK_SUBSET_PIN }, + { "PH-NETSUB PUK", MM_MOBILE_ERROR_NETWORK_SUBSET_PUK }, + { "PH-SP PIN", MM_MOBILE_ERROR_SERVICE_PIN }, + { "PH-SP PUK", MM_MOBILE_ERROR_SERVICE_PUK }, + { "PH-CORP PIN", MM_MOBILE_ERROR_CORP_PIN }, + { "PH-CORP PUK", MM_MOBILE_ERROR_CORP_PUK }, + { NULL, MM_MOBILE_ERROR_PHONE_FAILURE }, + }; + + if (error) + info->error = g_error_copy (error); + else if (g_str_has_prefix (response->str, "+CPIN: ")) { + const char *str = response->str + 7; + + if (g_str_has_prefix (str, "READY")) + parsed = TRUE; + else { + CPinResult *iter = &results[0]; + + /* Translate the error */ + while (iter->result) { + if (g_str_has_prefix (str, iter->result)) { + info->error = mm_mobile_error_for_code (iter->code); + parsed = TRUE; + break; + } + iter++; + } + } + } + + if (!info->error && !parsed) { + info->error = g_error_new (MM_MODEM_ERROR, + MM_MODEM_ERROR_GENERAL, + "Could not parse PIN request response '%s'", + response->str); + } + + mm_callback_info_schedule (info); +} + +void +mm_generic_gsm_check_pin (MMGenericGsm *modem, + MMModemFn callback, + gpointer user_data) +{ + MMGenericGsmPrivate *priv; + MMCallbackInfo *info; + + g_return_if_fail (MM_IS_GENERIC_GSM (modem)); + + priv = MM_GENERIC_GSM_GET_PRIVATE (modem); + info = mm_callback_info_new (MM_MODEM (modem), callback, user_data); + mm_serial_port_queue_command (priv->primary, "+CPIN?", 3, pin_check_done, info); +} + +/*****************************************************************************/ + +void +mm_generic_gsm_update_enabled_state (MMGenericGsm *self, + gboolean stay_connected, + MMModemStateReason reason) +{ + MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (self); + + /* While connected we don't want registration status changes to change + * the modem's state away from CONNECTED. + */ + if (stay_connected && (mm_modem_get_state (MM_MODEM (self)) >= MM_MODEM_STATE_DISCONNECTING)) + return; + + switch (priv->reg_status) { + case MM_MODEM_GSM_NETWORK_REG_STATUS_HOME: + case MM_MODEM_GSM_NETWORK_REG_STATUS_ROAMING: + mm_modem_set_state (MM_MODEM (self), MM_MODEM_STATE_REGISTERED, reason); + break; + case MM_MODEM_GSM_NETWORK_REG_STATUS_SEARCHING: + mm_modem_set_state (MM_MODEM (self), MM_MODEM_STATE_SEARCHING, reason); + break; + case MM_MODEM_GSM_NETWORK_REG_STATUS_IDLE: + case MM_MODEM_GSM_NETWORK_REG_STATUS_DENIED: + case MM_MODEM_GSM_NETWORK_REG_STATUS_UNKNOWN: + default: + mm_modem_set_state (MM_MODEM (self), MM_MODEM_STATE_ENABLED, reason); + break; + } +} + +static void +check_valid (MMGenericGsm *self) +{ + MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (self); + gboolean new_valid = FALSE; + + if (priv->primary && priv->data) + new_valid = TRUE; + + mm_modem_base_set_valid (MM_MODEM_BASE (self), new_valid); +} + +static gboolean +owns_port (MMModem *modem, const char *subsys, const char *name) +{ + return !!mm_modem_base_get_port (MM_MODEM_BASE (modem), subsys, name); +} + +MMPort * +mm_generic_gsm_grab_port (MMGenericGsm *self, + const char *subsys, + const char *name, + MMPortType ptype, + GError **error) +{ + MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (self); + MMPort *port = NULL; + GRegex *regex; + + g_return_val_if_fail (!strcmp (subsys, "net") || !strcmp (subsys, "tty"), FALSE); + + port = mm_modem_base_add_port (MM_MODEM_BASE (self), subsys, name, ptype); + if (port && MM_IS_SERIAL_PORT (port)) { + mm_serial_port_set_response_parser (MM_SERIAL_PORT (port), + mm_serial_parser_v1_parse, + mm_serial_parser_v1_new (), + mm_serial_parser_v1_destroy); + + regex = g_regex_new ("\\r\\n\\+CREG: (\\d+)\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); + mm_serial_port_add_unsolicited_msg_handler (MM_SERIAL_PORT (port), regex, reg_state_changed, self, NULL); + g_regex_unref (regex); + + if (ptype == MM_PORT_TYPE_PRIMARY) { + priv->primary = MM_SERIAL_PORT (port); + if (!priv->data) { + priv->data = port; + g_object_notify (G_OBJECT (self), MM_MODEM_DATA_DEVICE); + } + check_valid (self); + } else if (ptype == MM_PORT_TYPE_SECONDARY) + priv->secondary = MM_SERIAL_PORT (port); + } else { + /* Net device (if any) is the preferred data port */ + if (!priv->data || MM_IS_SERIAL_PORT (priv->data)) { + priv->data = port; + g_object_notify (G_OBJECT (self), MM_MODEM_DATA_DEVICE); + check_valid (self); + } + } + + return port; +} + +static gboolean +grab_port (MMModem *modem, + const char *subsys, + const char *name, + MMPortType suggested_type, + gpointer user_data, + GError **error) +{ + MMGenericGsm *self = MM_GENERIC_GSM (modem); + MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (self); + MMPortType ptype = MM_PORT_TYPE_IGNORED; + + if (priv->primary) + g_return_val_if_fail (suggested_type != MM_PORT_TYPE_PRIMARY, FALSE); + + if (!strcmp (subsys, "tty")) { + if (suggested_type != MM_PORT_TYPE_UNKNOWN) + ptype = suggested_type; + else { + if (!priv->primary) + ptype = MM_PORT_TYPE_PRIMARY; + else if (!priv->secondary) + ptype = MM_PORT_TYPE_SECONDARY; + } + } + + return !!mm_generic_gsm_grab_port (self, subsys, name, ptype, error); +} + +static void +release_port (MMModem *modem, const char *subsys, const char *name) +{ + MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (modem); + MMPort *port; + + if (strcmp (subsys, "tty") && strcmp (subsys, "net")) + return; + + port = mm_modem_base_get_port (MM_MODEM_BASE (modem), subsys, name); + if (!port) + return; + + if (port == MM_PORT (priv->primary)) { + mm_modem_base_remove_port (MM_MODEM_BASE (modem), port); + priv->primary = NULL; + } + + if (port == priv->data) { + priv->data = NULL; + g_object_notify (G_OBJECT (modem), MM_MODEM_DATA_DEVICE); + } + + if (port == MM_PORT (priv->secondary)) { + mm_modem_base_remove_port (MM_MODEM_BASE (modem), port); + priv->secondary = NULL; + } + + check_valid (MM_GENERIC_GSM (modem)); +} + +void +mm_generic_gsm_enable_complete (MMGenericGsm *modem, + GError *error, + MMCallbackInfo *info) +{ + g_return_if_fail (modem != NULL); + g_return_if_fail (MM_IS_GENERIC_GSM (modem)); + g_return_if_fail (info != NULL); + + if (error) { + mm_modem_set_state (MM_MODEM (modem), + MM_MODEM_STATE_DISABLED, + MM_MODEM_STATE_REASON_NONE); + + info->error = g_error_copy (error); + } else { + /* Modem is enabled; update the state */ + mm_generic_gsm_update_enabled_state (modem, FALSE, MM_MODEM_STATE_REASON_NONE); + } + + mm_callback_info_schedule (info); +} + +static void +enable_done (MMSerialPort *port, + GString *response, + GError *error, + gpointer user_data) +{ + MMCallbackInfo *info = (MMCallbackInfo *) user_data; + + /* Ignore power-up command errors, not all devices actually support + * CFUN=1. + */ + /* FIXME: instead of just ignoring errors, since we allow subclasses + * to override the power-on command, make a class function for powering + * on the phone and let the subclass decided whether it wants to handle + * errors or ignore them. + */ + + mm_generic_gsm_enable_complete (MM_GENERIC_GSM (info->modem), NULL, info); +} + +static void +init_done (MMSerialPort *port, + GString *response, + GError *error, + gpointer user_data) +{ + MMCallbackInfo *info = (MMCallbackInfo *) user_data; + char *cmd = NULL; + + if (error) { + mm_generic_gsm_enable_complete (MM_GENERIC_GSM (info->modem), error, info); + return; + } + + /* Ensure echo is off after the init command; some modems ignore the + * E0 when it's in the same like as ATZ (Option GIO322). + */ + mm_serial_port_queue_command (port, "E0 +CMEE=1", 2, NULL, NULL); + + g_object_get (G_OBJECT (info->modem), MM_GENERIC_GSM_INIT_CMD_OPTIONAL, &cmd, NULL); + mm_serial_port_queue_command (port, cmd, 2, NULL, NULL); + g_free (cmd); + + if (MM_GENERIC_GSM_GET_PRIVATE (info->modem)->unsolicited_registration) + mm_serial_port_queue_command (port, "+CREG=1", 5, NULL, NULL); + else + mm_serial_port_queue_command (port, "+CREG=0", 5, NULL, NULL); + + g_object_get (G_OBJECT (info->modem), MM_GENERIC_GSM_POWER_UP_CMD, &cmd, NULL); + if (cmd && strlen (cmd)) + mm_serial_port_queue_command (port, cmd, 5, enable_done, user_data); + else + enable_done (port, NULL, NULL, user_data); + g_free (cmd); +} + +static void +enable_flash_done (MMSerialPort *port, GError *error, gpointer user_data) +{ + MMCallbackInfo *info = user_data; + char *cmd = NULL; + + if (error) { + mm_generic_gsm_enable_complete (MM_GENERIC_GSM (info->modem), error, info); + return; + } + + g_object_get (G_OBJECT (info->modem), MM_GENERIC_GSM_INIT_CMD, &cmd, NULL); + mm_serial_port_queue_command (port, cmd, 3, init_done, user_data); + g_free (cmd); +} + +static void +real_do_enable (MMGenericGsm *self, MMModemFn callback, gpointer user_data) +{ + MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (self); + MMCallbackInfo *info; + + info = mm_callback_info_new (MM_MODEM (self), callback, user_data); + mm_serial_port_flash (priv->primary, 100, enable_flash_done, info); +} + +static void +enable (MMModem *modem, + MMModemFn callback, + gpointer user_data) +{ + MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (modem); + GError *error = NULL; + + /* First, reset the previously used CID */ + mm_generic_gsm_set_cid (MM_GENERIC_GSM (modem), 0); + + if (!mm_serial_port_open (priv->primary, &error)) { + MMCallbackInfo *info; + + g_assert (error); + info = mm_callback_info_new (modem, callback, user_data); + info->error = error; + mm_callback_info_schedule (info); + return; + } + + mm_modem_set_state (modem, MM_MODEM_STATE_ENABLING, MM_MODEM_STATE_REASON_NONE); + + g_assert (MM_GENERIC_GSM_GET_CLASS (modem)->do_enable); + MM_GENERIC_GSM_GET_CLASS (modem)->do_enable (MM_GENERIC_GSM (modem), callback, user_data); +} + +static void +disable_done (MMSerialPort *port, + GString *response, + GError *error, + gpointer user_data) +{ + MMCallbackInfo *info = user_data; + + info->error = mm_modem_check_removed (info->modem, error); + if (!info->error) { + MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (info->modem); + + mm_serial_port_close (port); + mm_modem_set_state (MM_MODEM (info->modem), + MM_MODEM_STATE_DISABLED, + MM_MODEM_STATE_REASON_NONE); + priv->reg_status = MM_MODEM_GSM_NETWORK_REG_STATUS_UNKNOWN; + } + mm_callback_info_schedule (info); +} + +static void +disable_flash_done (MMSerialPort *port, + GError *error, + gpointer user_data) +{ + MMCallbackInfo *info = user_data; + MMModemState prev_state; + char *cmd = NULL; + + info->error = mm_modem_check_removed (info->modem, error); + if (info->error) { + if (info->modem) { + /* Reset old state since the operation failed */ + prev_state = GPOINTER_TO_UINT (mm_callback_info_get_data (info, MM_GENERIC_GSM_PREV_STATE_TAG)); + mm_modem_set_state (MM_MODEM (info->modem), + prev_state, + MM_MODEM_STATE_REASON_NONE); + } + + mm_callback_info_schedule (info); + return; + } + + g_object_get (G_OBJECT (info->modem), MM_GENERIC_GSM_POWER_DOWN_CMD, &cmd, NULL); + if (cmd && strlen (cmd)) + mm_serial_port_queue_command (port, cmd, 5, disable_done, user_data); + else + disable_done (port, NULL, NULL, user_data); + g_free (cmd); +} + +static void +disable (MMModem *modem, + MMModemFn callback, + gpointer user_data) +{ + MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (modem); + MMCallbackInfo *info; + MMModemState state; + + /* First, reset the previously used CID and clean up registration */ + mm_generic_gsm_set_cid (MM_GENERIC_GSM (modem), 0); + mm_generic_gsm_pending_registration_stop (MM_GENERIC_GSM (modem)); + + info = mm_callback_info_new (modem, callback, user_data); + + /* Cache the previous state so we can reset it if the operation fails */ + state = mm_modem_get_state (modem); + mm_callback_info_set_data (info, + MM_GENERIC_GSM_PREV_STATE_TAG, + GUINT_TO_POINTER (state), + NULL); + + mm_modem_set_state (MM_MODEM (info->modem), + MM_MODEM_STATE_DISABLING, + MM_MODEM_STATE_REASON_NONE); + + if (mm_port_get_connected (MM_PORT (priv->primary))) + mm_serial_port_flash (priv->primary, 1000, disable_flash_done, info); + else + disable_flash_done (priv->primary, NULL, info); +} + +static void +get_string_done (MMSerialPort *port, + GString *response, + GError *error, + gpointer user_data) +{ + MMCallbackInfo *info = (MMCallbackInfo *) user_data; + + if (error) + info->error = g_error_copy (error); + else + mm_callback_info_set_result (info, g_strdup (response->str), g_free); + + mm_callback_info_schedule (info); +} + +static void +get_imei (MMModemGsmCard *modem, + MMModemStringFn callback, + gpointer user_data) +{ + MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (modem); + MMCallbackInfo *info; + + info = mm_callback_info_string_new (MM_MODEM (modem), callback, user_data); + mm_serial_port_queue_command_cached (priv->primary, "+CGSN", 3, get_string_done, info); +} + +static void +get_imsi (MMModemGsmCard *modem, + MMModemStringFn callback, + gpointer user_data) +{ + MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (modem); + MMCallbackInfo *info; + + info = mm_callback_info_string_new (MM_MODEM (modem), callback, user_data); + mm_serial_port_queue_command_cached (priv->primary, "+CIMI", 3, get_string_done, info); +} + +static void +card_info_invoke (MMCallbackInfo *info) +{ + MMModemInfoFn callback = (MMModemInfoFn) info->callback; + + callback (info->modem, + (char *) mm_callback_info_get_data (info, "card-info-manufacturer"), + (char *) mm_callback_info_get_data (info, "card-info-model"), + (char *) mm_callback_info_get_data (info, "card-info-version"), + info->error, info->user_data); +} + +#define GMI_RESP_TAG "+CGMI:" +#define GMM_RESP_TAG "+CGMM:" +#define GMR_RESP_TAG "+CGMR:" + +static const char * +strip_tag (const char *str, const char *tag) +{ + /* Strip the response header, if any */ + if (strncmp (str, tag, strlen (tag)) == 0) + str += strlen (tag); + while (*str && isspace (*str)) + str++; + return str; +} + +static void +get_version_done (MMSerialPort *port, + GString *response, + GError *error, + gpointer user_data) +{ + MMCallbackInfo *info = (MMCallbackInfo *) user_data; + const char *resp = strip_tag (response->str, GMR_RESP_TAG); + + if (!error) + mm_callback_info_set_data (info, "card-info-version", g_strdup (resp), g_free); + else if (!info->error) + info->error = g_error_copy (error); + + mm_callback_info_schedule (info); +} + +static void +get_model_done (MMSerialPort *port, + GString *response, + GError *error, + gpointer user_data) +{ + MMCallbackInfo *info = (MMCallbackInfo *) user_data; + const char *resp = strip_tag (response->str, GMM_RESP_TAG); + + if (!error) + mm_callback_info_set_data (info, "card-info-model", g_strdup (resp), g_free); + else if (!info->error) + info->error = g_error_copy (error); +} + +static void +get_manufacturer_done (MMSerialPort *port, + GString *response, + GError *error, + gpointer user_data) +{ + MMCallbackInfo *info = (MMCallbackInfo *) user_data; + const char *resp = strip_tag (response->str, GMI_RESP_TAG); + + if (!error) + mm_callback_info_set_data (info, "card-info-manufacturer", g_strdup (resp), g_free); + else + info->error = g_error_copy (error); +} + +static void +get_card_info (MMModem *modem, + MMModemInfoFn callback, + gpointer user_data) +{ + MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (modem); + MMCallbackInfo *info; + + info = mm_callback_info_new_full (MM_MODEM (modem), + card_info_invoke, + G_CALLBACK (callback), + user_data); + + mm_serial_port_queue_command_cached (priv->primary, "+CGMI", 3, get_manufacturer_done, info); + mm_serial_port_queue_command_cached (priv->primary, "+CGMM", 3, get_model_done, info); + mm_serial_port_queue_command_cached (priv->primary, "+CGMR", 3, get_version_done, info); +} + +static void +send_puk_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); +} + +static void +send_puk (MMModemGsmCard *modem, + const char *puk, + const char *pin, + MMModemFn callback, + gpointer user_data) +{ + MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (modem); + MMCallbackInfo *info; + char *command; + + info = mm_callback_info_new (MM_MODEM (modem), callback, user_data); + command = g_strdup_printf ("+CPIN=\"%s\",\"%s\"", puk, pin); + mm_serial_port_queue_command (priv->primary, command, 3, send_puk_done, info); + g_free (command); +} + +static void +send_pin_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); +} + +static void +send_pin (MMModemGsmCard *modem, + const char *pin, + MMModemFn callback, + gpointer user_data) +{ + MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (modem); + MMCallbackInfo *info; + char *command; + + info = mm_callback_info_new (MM_MODEM (modem), callback, user_data); + command = g_strdup_printf ("+CPIN=\"%s\"", pin); + mm_serial_port_queue_command (priv->primary, command, 3, send_pin_done, info); + g_free (command); +} + +static void +enable_pin_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); +} + +static void +enable_pin (MMModemGsmCard *modem, + const char *pin, + gboolean enabled, + MMModemFn callback, + gpointer user_data) +{ + MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (modem); + MMCallbackInfo *info; + char *command; + + info = mm_callback_info_new (MM_MODEM (modem), callback, user_data); + command = g_strdup_printf ("+CLCK=\"SC\",%d,\"%s\"", enabled ? 1 : 0, pin); + mm_serial_port_queue_command (priv->primary, command, 3, enable_pin_done, info); + g_free (command); +} + +static void +change_pin_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); +} + +static void +change_pin (MMModemGsmCard *modem, + const char *old_pin, + const char *new_pin, + MMModemFn callback, + gpointer user_data) +{ + MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (modem); + MMCallbackInfo *info; + char *command; + + info = mm_callback_info_new (MM_MODEM (modem), callback, user_data); + command = g_strdup_printf ("+CPWD=\"SC\",\"%s\",\"%s\"", old_pin, new_pin); + mm_serial_port_queue_command (priv->primary, command, 3, change_pin_done, info); + g_free (command); +} + +static char * +parse_operator (const char *reply) +{ + char *operator = NULL; + + if (reply && !strncmp (reply, "+COPS: ", 7)) { + /* Got valid reply */ + GRegex *r; + GMatchInfo *match_info; + + reply += 7; + r = g_regex_new ("(\\d),(\\d),\"(.+)\"", G_REGEX_UNGREEDY, 0, NULL); + if (!r) + return NULL; + + g_regex_match (r, reply, 0, &match_info); + if (g_match_info_matches (match_info)) + operator = g_match_info_fetch (match_info, 3); + + g_match_info_free (match_info); + g_regex_unref (r); + } + + return operator; +} + +static void +read_operator_code_done (MMSerialPort *port, + GString *response, + GError *error, + gpointer user_data) +{ + MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (user_data); + char *oper; + + if (error) + return; + + oper = parse_operator (response->str); + if (!oper) + return; + + g_free (priv->oper_code); + priv->oper_code = oper; +} + +static void +read_operator_name_done (MMSerialPort *port, + GString *response, + GError *error, + gpointer user_data) +{ + MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (user_data); + char *oper; + + if (error) + return; + + oper = parse_operator (response->str); + if (!oper) + return; + + g_free (priv->oper_name); + priv->oper_name = oper; + + mm_modem_gsm_network_registration_info (MM_MODEM_GSM_NETWORK (user_data), + priv->reg_status, + priv->oper_code, + priv->oper_name); +} + +/* Registration */ +#define REG_STATUS_AGAIN_TAG "reg-status-again" + +void +mm_generic_gsm_pending_registration_stop (MMGenericGsm *modem) +{ + MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (modem); + + if (priv->pending_reg_id) { + /* Clear the registration timeout handler */ + g_source_remove (priv->pending_reg_id); + priv->pending_reg_id = 0; + } + + if (priv->pending_reg_info) { + /* Clear any ongoing registration status callback */ + mm_callback_info_set_data (priv->pending_reg_info, REG_STATUS_AGAIN_TAG, NULL, NULL); + + /* And schedule the callback */ + mm_callback_info_schedule (priv->pending_reg_info); + priv->pending_reg_info = NULL; + } +} + +static gboolean +reg_status_updated (MMGenericGsm *self, int new_value, GError **error) +{ + MMModemGsmNetworkRegStatus status; + gboolean status_done = FALSE; + + switch (new_value) { + case 0: + status = MM_MODEM_GSM_NETWORK_REG_STATUS_IDLE; + break; + case 1: + status = MM_MODEM_GSM_NETWORK_REG_STATUS_HOME; + break; + case 2: + status = MM_MODEM_GSM_NETWORK_REG_STATUS_SEARCHING; + break; + case 3: + status = MM_MODEM_GSM_NETWORK_REG_STATUS_DENIED; + break; + case 4: + status = MM_MODEM_GSM_NETWORK_REG_STATUS_UNKNOWN; + break; + case 5: + status = MM_MODEM_GSM_NETWORK_REG_STATUS_ROAMING; + break; + default: + status = MM_MODEM_GSM_NETWORK_REG_STATUS_UNKNOWN; + break; + } + + mm_generic_gsm_set_reg_status (self, status); + + /* Registration has either completed successfully or completely failed */ + switch (status) { + case MM_MODEM_GSM_NETWORK_REG_STATUS_HOME: + case MM_MODEM_GSM_NETWORK_REG_STATUS_ROAMING: + /* Successfully registered - stop registration */ + status_done = TRUE; + break; + case MM_MODEM_GSM_NETWORK_REG_STATUS_DENIED: + /* registration failed - stop registration */ + if (error) + *error = mm_mobile_error_for_code (MM_MOBILE_ERROR_NETWORK_NOT_ALLOWED); + status_done = TRUE; + break; + case MM_MODEM_GSM_NETWORK_REG_STATUS_SEARCHING: + if (error) + *error = mm_mobile_error_for_code (MM_MOBILE_ERROR_NETWORK_TIMEOUT); + break; + case MM_MODEM_GSM_NETWORK_REG_STATUS_IDLE: + if (error) + *error = mm_mobile_error_for_code (MM_MOBILE_ERROR_NO_NETWORK); + break; + default: + if (error) + *error = mm_mobile_error_for_code (MM_MOBILE_ERROR_UNKNOWN); + break; + } + return status_done; +} + +static void +reg_state_changed (MMSerialPort *port, + GMatchInfo *match_info, + gpointer user_data) +{ + MMGenericGsm *self = MM_GENERIC_GSM (user_data); + MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (self); + char *str; + gboolean done; + + str = g_match_info_fetch (match_info, 1); + done = reg_status_updated (self, atoi (str), NULL); + g_free (str); + + if (done) { + /* If registration is finished (either registered or failed) but the + * registration query hasn't completed yet, just remove the timeout and + * let the registration query complete. + */ + if (priv->pending_reg_id) { + g_source_remove (priv->pending_reg_id); + priv->pending_reg_id = 0; + } + } +} + +static gboolean +reg_status_again (gpointer data) +{ + MMCallbackInfo *info = (MMCallbackInfo *) data; + MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (info->modem); + + g_warn_if_fail (info == priv->pending_reg_info); + + if (priv->pending_reg_info) + get_registration_status (priv->primary, info); + + return FALSE; +} + +static void +reg_status_again_remove (gpointer data) +{ + guint id = GPOINTER_TO_UINT (data); + + /* Technically the GSource ID can be 0, but in practice it won't be */ + if (id > 0) + g_source_remove (id); +} + +static void +get_reg_status_done (MMSerialPort *port, + GString *response, + GError *error, + gpointer user_data) +{ + MMCallbackInfo *info = (MMCallbackInfo *) user_data; + MMGenericGsm *self = MM_GENERIC_GSM (info->modem); + MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (self); + int reg_status = -1; + GRegex *r; + GMatchInfo *match_info; + char *tmp; + guint id; + + g_warn_if_fail (info == priv->pending_reg_info); + + if (error) { + info->error = g_error_copy (error); + goto reg_done; + } + + r = g_regex_new ("\\+CREG:\\s*(\\d+),\\s*(\\d+)", + G_REGEX_RAW | G_REGEX_OPTIMIZE, + 0, &info->error); + if (r) { + g_regex_match_full (r, response->str, response->len, 0, 0, &match_info, &info->error); + if (g_match_info_matches (match_info)) { + /* Get reg status */ + tmp = g_match_info_fetch (match_info, 2); + if (isdigit (tmp[0])) { + tmp[1] = '\0'; + reg_status = atoi (tmp); + } else { + info->error = g_error_new (MM_MODEM_ERROR, + MM_MODEM_ERROR_GENERAL, + "Unknown registration status '%s'", + tmp); + } + g_free (tmp); + } else { + info->error = g_error_new_literal (MM_MODEM_ERROR, + MM_MODEM_ERROR_GENERAL, + "Could not parse the registration status response"); + } + g_match_info_free (match_info); + g_regex_unref (r); + } + + if ( reg_status >= 0 + && !reg_status_updated (self, reg_status, &info->error) + && priv->pending_reg_info) { + g_clear_error (&info->error); + + /* Not registered yet; poll registration status again */ + id = g_timeout_add_seconds (1, reg_status_again, info); + mm_callback_info_set_data (info, REG_STATUS_AGAIN_TAG, + GUINT_TO_POINTER (id), + reg_status_again_remove); + return; + } + +reg_done: + /* This will schedule the callback for us */ + mm_generic_gsm_pending_registration_stop (self); +} + +static void +get_registration_status (MMSerialPort *port, MMCallbackInfo *info) +{ + MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (info->modem); + + g_warn_if_fail (info == priv->pending_reg_info); + + mm_serial_port_queue_command (port, "+CREG?", 10, get_reg_status_done, info); +} + +static void +register_done (MMSerialPort *port, + GString *response, + GError *error, + gpointer user_data) +{ + MMCallbackInfo *info = user_data; + MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (info->modem); + + mm_callback_info_unref (info); + + /* If the registration timed out (and thus pending_reg_info will be NULL) + * and the modem eventually got around to sending the response for the + * registration request then just ignore the response since the callback is + * already called. + */ + + if (priv->pending_reg_info) { + g_warn_if_fail (info == priv->pending_reg_info); + + /* Ignore errors here, get the actual registration status */ + get_registration_status (port, info); + } +} + +static gboolean +registration_timed_out (gpointer data) +{ + MMCallbackInfo *info = (MMCallbackInfo *) data; + MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (info->modem); + + g_warn_if_fail (info == priv->pending_reg_info); + + priv->reg_status = MM_MODEM_GSM_NETWORK_REG_STATUS_IDLE; + + info->error = mm_mobile_error_for_code (MM_MOBILE_ERROR_NETWORK_TIMEOUT); + mm_generic_gsm_pending_registration_stop (MM_GENERIC_GSM (info->modem)); + + return FALSE; +} + +static void +do_register (MMModemGsmNetwork *modem, + const char *network_id, + MMModemFn callback, + gpointer user_data) +{ + MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (modem); + MMCallbackInfo *info; + char *command; + + /* Clear any previous registration */ + mm_generic_gsm_pending_registration_stop (MM_GENERIC_GSM (modem)); + + info = mm_callback_info_new (MM_MODEM (modem), callback, user_data); + + priv->pending_reg_id = g_timeout_add_seconds (60, registration_timed_out, info); + priv->pending_reg_info = info; + + if (network_id) + command = g_strdup_printf ("+COPS=1,2,\"%s\"", network_id); + else + command = g_strdup ("+COPS=0,,"); + + /* Ref the callback info to ensure it stays alive for register_done() even + * if the timeout triggers and ends registration (which calls the callback + * and unrefs the callback info). Some devices (hso) will delay the + * registration response until the registration is done (and thus + * unsolicited registration responses will arrive before the +COPS is + * complete). Most other devices will return the +COPS response immediately + * and the unsolicited response (if any) at a later time. + * + * To handle both these cases, unsolicited registration responses will just + * remove the pending registration timeout but we let the +COPS command + * complete. For those devices that delay the +COPS response (hso) the + * callback will be called from register_done(). For those devices that + * return the +COPS response immediately, we'll poll the registration state + * and call the callback from get_reg_status_done() in response to the + * polled response. The registration timeout will only be triggered when + * the +COPS response is never received. + */ + mm_callback_info_ref (info); + mm_serial_port_queue_command (priv->primary, command, 120, register_done, info); + g_free (command); +} + +static void +gsm_network_reg_info_invoke (MMCallbackInfo *info) +{ + MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (info->modem); + MMModemGsmNetworkRegInfoFn callback = (MMModemGsmNetworkRegInfoFn) info->callback; + + callback (MM_MODEM_GSM_NETWORK (info->modem), + priv->reg_status, + priv->oper_code, + priv->oper_name, + info->error, + info->user_data); +} + +static void +get_registration_info (MMModemGsmNetwork *self, + MMModemGsmNetworkRegInfoFn callback, + gpointer user_data) +{ + MMCallbackInfo *info; + + info = mm_callback_info_new_full (MM_MODEM (self), + gsm_network_reg_info_invoke, + G_CALLBACK (callback), + user_data); + + mm_callback_info_schedule (info); +} + +void +mm_generic_gsm_connect_complete (MMGenericGsm *modem, + GError *error, + MMCallbackInfo *info) +{ + MMGenericGsmPrivate *priv; + + g_return_if_fail (modem != NULL); + g_return_if_fail (MM_IS_GENERIC_GSM (modem)); + g_return_if_fail (info != NULL); + + priv = MM_GENERIC_GSM_GET_PRIVATE (modem); + + if (error) { + mm_generic_gsm_update_enabled_state (modem, FALSE, MM_MODEM_STATE_REASON_NONE); + info->error = g_error_copy (error); + } else { + /* Modem is connected; update the state */ + mm_port_set_connected (priv->data, TRUE); + mm_modem_set_state (MM_MODEM (modem), + MM_MODEM_STATE_CONNECTED, + MM_MODEM_STATE_REASON_NONE); + } + + mm_callback_info_schedule (info); +} + +static void +connect_report_done (MMSerialPort *port, + GString *response, + GError *error, + gpointer user_data) +{ + MMCallbackInfo *info = (MMCallbackInfo *) user_data; + GError *real_error; + + /* If the CEER command was successful, copy that error reason into the + * callback's error. If not, use the original error. + */ + + /* Have to do this little dance since mm_generic_gsm_connect_complete() + * copies the provided error into the callback info. + */ + real_error = info->error; + info->error = NULL; + + if ( !error + && g_str_has_prefix (response->str, "+CEER: ") + && (strlen (response->str) > 7)) { + /* copy the connect failure reason into the error */ + g_free (real_error->message); + real_error->message = g_strdup (response->str + 7); /* skip the "+CEER: " */ + } + + mm_generic_gsm_connect_complete (MM_GENERIC_GSM (info->modem), real_error, info); + g_error_free (real_error); +} + +static void +connect_done (MMSerialPort *port, + GString *response, + GError *error, + gpointer user_data) +{ + MMCallbackInfo *info = (MMCallbackInfo *) user_data; + MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (info->modem); + + if (error) { + info->error = g_error_copy (error); + /* Try to get more information why it failed */ + priv = MM_GENERIC_GSM_GET_PRIVATE (info->modem); + mm_serial_port_queue_command (priv->primary, "+CEER", 3, connect_report_done, info); + } else + mm_generic_gsm_connect_complete (MM_GENERIC_GSM (info->modem), NULL, info); +} + +static void +connect (MMModem *modem, + const char *number, + MMModemFn callback, + gpointer user_data) +{ + MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (modem); + MMCallbackInfo *info; + char *command; + guint32 cid = mm_generic_gsm_get_cid (MM_GENERIC_GSM (modem)); + + info = mm_callback_info_new (modem, callback, user_data); + + mm_modem_set_state (modem, MM_MODEM_STATE_CONNECTING, MM_MODEM_STATE_REASON_NONE); + + if (cid > 0) { + GString *str; + + str = g_string_new ("D"); + if (g_str_has_suffix (number, "#")) + str = g_string_append_len (str, number, strlen (number) - 1); + else + str = g_string_append (str, number); + + g_string_append_printf (str, "***%d#", cid); + command = g_string_free (str, FALSE); + } else + command = g_strconcat ("DT", number, NULL); + + mm_serial_port_queue_command (priv->primary, command, 60, connect_done, info); + g_free (command); +} + +static void +disconnect_flash_done (MMSerialPort *port, + GError *error, + gpointer user_data) +{ + MMCallbackInfo *info = (MMCallbackInfo *) user_data; + MMModemState prev_state; + + info->error = mm_modem_check_removed (info->modem, error); + if (info->error) { + if (info->modem) { + /* Reset old state since the operation failed */ + prev_state = GPOINTER_TO_UINT (mm_callback_info_get_data (info, MM_GENERIC_GSM_PREV_STATE_TAG)); + mm_modem_set_state (MM_MODEM (info->modem), + prev_state, + MM_MODEM_STATE_REASON_NONE); + } + } else { + MMGenericGsm *self = MM_GENERIC_GSM (info->modem); + MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (self); + + mm_port_set_connected (priv->data, FALSE); + mm_generic_gsm_update_enabled_state (self, FALSE, MM_MODEM_STATE_REASON_NONE); + } + + mm_callback_info_schedule (info); +} + +static void +disconnect (MMModem *modem, + MMModemFn callback, + gpointer user_data) +{ + MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (modem); + MMCallbackInfo *info; + MMModemState state; + + /* First, reset the previously used CID */ + mm_generic_gsm_set_cid (MM_GENERIC_GSM (modem), 0); + + info = mm_callback_info_new (modem, callback, user_data); + + /* Cache the previous state so we can reset it if the operation fails */ + state = mm_modem_get_state (modem); + mm_callback_info_set_data (info, + MM_GENERIC_GSM_PREV_STATE_TAG, + GUINT_TO_POINTER (state), + NULL); + + mm_modem_set_state (modem, MM_MODEM_STATE_DISCONNECTING, MM_MODEM_STATE_REASON_NONE); + mm_serial_port_flash (priv->primary, 1000, disconnect_flash_done, info); +} + +static void +gsm_network_scan_invoke (MMCallbackInfo *info) +{ + MMModemGsmNetworkScanFn callback = (MMModemGsmNetworkScanFn) info->callback; + + callback (MM_MODEM_GSM_NETWORK (info->modem), + (GPtrArray *) mm_callback_info_get_data (info, "scan-results"), + info->error, + info->user_data); +} + +static void +scan_done (MMSerialPort *port, + GString *response, + GError *error, + gpointer user_data) +{ + MMCallbackInfo *info = (MMCallbackInfo *) user_data; + GPtrArray *results; + + if (error) + info->error = g_error_copy (error); + else { + results = mm_gsm_parse_scan_response (response->str, &info->error); + if (results) + mm_callback_info_set_data (info, "scan-results", results, mm_gsm_destroy_scan_data); + } + + mm_callback_info_schedule (info); +} + +static void +scan (MMModemGsmNetwork *modem, + MMModemGsmNetworkScanFn callback, + gpointer user_data) +{ + MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (modem); + MMCallbackInfo *info; + + info = mm_callback_info_new_full (MM_MODEM (modem), + gsm_network_scan_invoke, + G_CALLBACK (callback), + user_data); + + mm_serial_port_queue_command (priv->primary, "+COPS=?", 120, scan_done, info); +} + +/* SetApn */ + +static void +set_apn_done (MMSerialPort *port, + GString *response, + GError *error, + gpointer user_data) +{ + MMCallbackInfo *info = (MMCallbackInfo *) user_data; + + if (error) + info->error = g_error_copy (error); + else + mm_generic_gsm_set_cid (MM_GENERIC_GSM (info->modem), + GPOINTER_TO_UINT (mm_callback_info_get_data (info, "cid"))); + + mm_callback_info_schedule (info); +} + +static void +cid_range_read (MMSerialPort *port, + GString *response, + GError *error, + gpointer user_data) +{ + MMCallbackInfo *info = (MMCallbackInfo *) user_data; + guint32 cid = 0; + + if (error) + info->error = g_error_copy (error); + else if (g_str_has_prefix (response->str, "+CGDCONT: ")) { + GRegex *r; + GMatchInfo *match_info; + + r = g_regex_new ("\\+CGDCONT:\\s*\\((\\d+)-(\\d+)\\),\\(?\"(\\S+)\"", + G_REGEX_DOLLAR_ENDONLY | G_REGEX_RAW, + 0, &info->error); + if (r) { + g_regex_match_full (r, response->str, response->len, 0, 0, &match_info, &info->error); + while (cid == 0 && g_match_info_matches (match_info)) { + char *tmp; + + tmp = g_match_info_fetch (match_info, 3); + if (!strcmp (tmp, "IP")) { + int max_cid; + int highest_cid = GPOINTER_TO_INT (mm_callback_info_get_data (info, "highest-cid")); + + g_free (tmp); + + tmp = g_match_info_fetch (match_info, 2); + max_cid = atoi (tmp); + + if (highest_cid < max_cid) + cid = highest_cid + 1; + else + cid = highest_cid; + } + + g_free (tmp); + g_match_info_next (match_info, NULL); + } + + if (cid == 0) + /* Choose something */ + cid = 1; + } + } else + info->error = g_error_new_literal (MM_MODEM_ERROR, + MM_MODEM_ERROR_GENERAL, + "Could not parse the response"); + + if (info->error) + mm_callback_info_schedule (info); + else { + const char *apn = (const char *) mm_callback_info_get_data (info, "apn"); + char *command; + + mm_callback_info_set_data (info, "cid", GUINT_TO_POINTER (cid), NULL); + + command = g_strdup_printf ("+CGDCONT=%d,\"IP\",\"%s\"", cid, apn); + mm_serial_port_queue_command (port, command, 3, set_apn_done, info); + g_free (command); + } +} + +static void +existing_apns_read (MMSerialPort *port, + GString *response, + GError *error, + gpointer user_data) +{ + MMCallbackInfo *info = (MMCallbackInfo *) user_data; + gboolean found = FALSE; + + if (error) + info->error = g_error_copy (error); + else if (g_str_has_prefix (response->str, "+CGDCONT: ")) { + GRegex *r; + GMatchInfo *match_info; + + r = g_regex_new ("\\+CGDCONT:\\s*(\\d+)\\s*,\"(\\S+)\",\"(\\S+)\",\"(\\S*)\"", + G_REGEX_DOLLAR_ENDONLY | G_REGEX_RAW, + 0, &info->error); + if (r) { + const char *new_apn = (const char *) mm_callback_info_get_data (info, "apn"); + + g_regex_match_full (r, response->str, response->len, 0, 0, &match_info, &info->error); + while (!found && g_match_info_matches (match_info)) { + char *cid; + char *pdp_type; + char *apn; + int num_cid; + + cid = g_match_info_fetch (match_info, 1); + num_cid = atoi (cid); + pdp_type = g_match_info_fetch (match_info, 2); + apn = g_match_info_fetch (match_info, 3); + + if (!strcmp (apn, new_apn)) { + mm_generic_gsm_set_cid (MM_GENERIC_GSM (info->modem), (guint32) num_cid); + found = TRUE; + } + + if (!found && !strcmp (pdp_type, "IP")) { + int highest_cid; + + highest_cid = GPOINTER_TO_INT (mm_callback_info_get_data (info, "highest-cid")); + if (num_cid > highest_cid) + mm_callback_info_set_data (info, "highest-cid", GINT_TO_POINTER (num_cid), NULL); + } + + g_free (cid); + g_free (pdp_type); + g_free (apn); + g_match_info_next (match_info, NULL); + } + + g_match_info_free (match_info); + g_regex_unref (r); + } + } else if (strlen (response->str) == 0) { + /* No APNs configured, just don't set error */ + } else + info->error = g_error_new_literal (MM_MODEM_ERROR, + MM_MODEM_ERROR_GENERAL, + "Could not parse the response"); + + if (found || info->error) + mm_callback_info_schedule (info); + else + /* APN not configured on the card. Get the allowed CID range */ + mm_serial_port_queue_command_cached (port, "+CGDCONT=?", 3, cid_range_read, info); +} + +static void +set_apn (MMModemGsmNetwork *modem, + const char *apn, + MMModemFn callback, + gpointer user_data) +{ + MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (modem); + MMCallbackInfo *info; + + info = mm_callback_info_new (MM_MODEM (modem), callback, user_data); + mm_callback_info_set_data (info, "apn", g_strdup (apn), g_free); + + /* Start by searching if the APN is already in card */ + mm_serial_port_queue_command (priv->primary, "+CGDCONT?", 3, existing_apns_read, info); +} + +/* GetSignalQuality */ + +static void +get_signal_quality_done (MMSerialPort *port, + GString *response, + GError *error, + gpointer user_data) +{ + MMGenericGsmPrivate *priv; + MMCallbackInfo *info = (MMCallbackInfo *) user_data; + char *reply = response->str; + + if (error) + info->error = g_error_copy (error); + else if (!strncmp (reply, "+CSQ: ", 6)) { + /* Got valid reply */ + int quality; + int ber; + + reply += 6; + + if (sscanf (reply, "%d, %d", &quality, &ber)) { + /* 99 means unknown */ + if (quality == 99) { + info->error = g_error_new_literal (MM_MOBILE_ERROR, + MM_MOBILE_ERROR_NO_NETWORK, + "No service"); + } else { + /* Normalize the quality */ + quality = CLAMP (quality, 0, 31) * 100 / 31; + + priv = MM_GENERIC_GSM_GET_PRIVATE (info->modem); + priv->signal_quality = quality; + mm_callback_info_set_result (info, GUINT_TO_POINTER (quality), NULL); + } + } else + info->error = g_error_new_literal (MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL, + "Could not parse signal quality results"); + } + + mm_callback_info_schedule (info); +} + +static void +get_signal_quality (MMModemGsmNetwork *modem, + MMModemUIntFn callback, + gpointer user_data) +{ + MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (modem); + MMCallbackInfo *info; + gboolean connected; + + connected = mm_port_get_connected (MM_PORT (priv->primary)); + if (connected && !priv->secondary) { + g_message ("Returning saved signal quality %d", priv->signal_quality); + callback (MM_MODEM (modem), priv->signal_quality, NULL, user_data); + return; + } + + info = mm_callback_info_uint_new (MM_MODEM (modem), callback, user_data); + mm_serial_port_queue_command (connected ? priv->secondary : priv->primary, "+CSQ", 3, get_signal_quality_done, info); +} + +/*****************************************************************************/ +/* MMModemGsmSms interface */ + +static void +sms_send_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); +} + +static void +sms_send (MMModemGsmSms *modem, + const char *number, + const char *text, + const char *smsc, + guint validity, + guint class, + MMModemFn callback, + gpointer user_data) +{ + MMGenericGsmPrivate *priv; + MMCallbackInfo *info; + char *command; + gboolean connected; + MMSerialPort *port = NULL; + + info = mm_callback_info_new (MM_MODEM (modem), callback, user_data); + + priv = MM_GENERIC_GSM_GET_PRIVATE (info->modem); + connected = mm_port_get_connected (MM_PORT (priv->primary)); + if (connected) + port = priv->secondary; + else + port = priv->primary; + + if (!port) { + info->error = g_error_new_literal (MM_MODEM_ERROR, MM_MODEM_ERROR_CONNECTED, + "Cannot send SMS while connected"); + mm_callback_info_schedule (info); + return; + } + + /* FIXME: use the PDU mode instead */ + mm_serial_port_queue_command (port, "AT+CMGF=1", 3, NULL, NULL); + + command = g_strdup_printf ("+CMGS=\"%s\"\r%s\x1a", number, text); + mm_serial_port_queue_command (port, command, 10, sms_send_done, info); + g_free (command); +} + +MMSerialPort * +mm_generic_gsm_get_port (MMGenericGsm *modem, + MMPortType ptype) +{ + g_return_val_if_fail (MM_IS_GENERIC_GSM (modem), NULL); + g_return_val_if_fail (ptype != MM_PORT_TYPE_UNKNOWN, NULL); + + if (ptype == MM_PORT_TYPE_PRIMARY) + return MM_GENERIC_GSM_GET_PRIVATE (modem)->primary; + else if (ptype == MM_PORT_TYPE_SECONDARY) + return MM_GENERIC_GSM_GET_PRIVATE (modem)->secondary; + + return NULL; +} + +/*****************************************************************************/ +/* MMModemSimple interface */ + +typedef enum { + SIMPLE_STATE_BEGIN = 0, + SIMPLE_STATE_ENABLE, + SIMPLE_STATE_CHECK_PIN, + SIMPLE_STATE_REGISTER, + SIMPLE_STATE_SET_APN, + SIMPLE_STATE_CONNECT, + SIMPLE_STATE_DONE +} SimpleState; + +static const char * +simple_get_string_property (MMCallbackInfo *info, 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); + 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_state_machine (MMModem *modem, GError *error, gpointer user_data) +{ + MMCallbackInfo *info = (MMCallbackInfo *) user_data; + const char *str; + SimpleState state = GPOINTER_TO_UINT (mm_callback_info_get_data (info, "simple-connect-state")); + gboolean need_pin = FALSE; + + if (error) { + if (g_error_matches (error, MM_MOBILE_ERROR, MM_MOBILE_ERROR_SIM_PIN)) { + need_pin = TRUE; + state = SIMPLE_STATE_CHECK_PIN; + } else { + info->error = g_error_copy (error); + goto out; + } + } + + switch (state) { + case SIMPLE_STATE_BEGIN: + state = SIMPLE_STATE_ENABLE; + mm_modem_enable (modem, simple_state_machine, info); + break; + case SIMPLE_STATE_ENABLE: + state = SIMPLE_STATE_CHECK_PIN; + mm_generic_gsm_check_pin (MM_GENERIC_GSM (modem), simple_state_machine, info); + break; + case SIMPLE_STATE_CHECK_PIN: + if (need_pin) { + str = simple_get_string_property (info, "pin", &info->error); + if (str) + mm_modem_gsm_card_send_pin (MM_MODEM_GSM_CARD (modem), str, simple_state_machine, info); + else + info->error = g_error_copy (error); + } else { + str = simple_get_string_property (info, "network_id", &info->error); + state = SIMPLE_STATE_REGISTER; + if (!info->error) + mm_modem_gsm_network_register (MM_MODEM_GSM_NETWORK (modem), str, simple_state_machine, info); + } + break; + case SIMPLE_STATE_REGISTER: + str = simple_get_string_property (info, "apn", &info->error); + if (str) { + state = SIMPLE_STATE_SET_APN; + mm_modem_gsm_network_set_apn (MM_MODEM_GSM_NETWORK (modem), str, simple_state_machine, info); + break; + } + /* Fall through */ + case SIMPLE_STATE_SET_APN: + str = simple_get_string_property (info, "number", &info->error); + state = SIMPLE_STATE_CONNECT; + mm_modem_connect (modem, str, simple_state_machine, info); + break; + case SIMPLE_STATE_CONNECT: + state = SIMPLE_STATE_DONE; + break; + case SIMPLE_STATE_DONE: + break; + } + + 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); +} + +static void +simple_connect (MMModemSimple *simple, + GHashTable *properties, + MMModemFn callback, + gpointer user_data) +{ + MMCallbackInfo *info; + + 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); + + simple_state_machine (MM_MODEM (simple), NULL, info); +} + + + +static void +simple_free_gvalue (gpointer data) +{ + g_value_unset ((GValue *) data); + g_slice_free (GValue, data); +} + +static GValue * +simple_uint_value (guint32 i) +{ + GValue *val; + + val = g_slice_new0 (GValue); + g_value_init (val, G_TYPE_UINT); + g_value_set_uint (val, i); + + return val; +} + +static GValue * +simple_string_value (const char *str) +{ + GValue *val; + + val = g_slice_new0 (GValue); + g_value_init (val, G_TYPE_STRING); + g_value_set_string (val, str); + + return val; +} + +static void +simple_status_got_signal_quality (MMModem *modem, + guint32 result, + GError *error, + gpointer user_data) +{ + if (error) + g_warning ("Error getting signal quality: %s", error->message); + else + g_hash_table_insert ((GHashTable *) user_data, "signal_quality", simple_uint_value (result)); +} + +static void +simple_status_got_band (MMModem *modem, + guint32 result, + GError *error, + gpointer user_data) +{ + /* Ignore band errors since there's no generic implementation for it */ + if (!error) + g_hash_table_insert ((GHashTable *) user_data, "band", simple_uint_value (result)); +} + +static void +simple_status_got_mode (MMModem *modem, + guint32 result, + GError *error, + gpointer user_data) +{ + /* Ignore network mode errors since there's no generic implementation for it */ + if (!error) + g_hash_table_insert ((GHashTable *) user_data, "network_mode", simple_uint_value (result)); +} + +static void +simple_status_got_reg_info (MMModemGsmNetwork *modem, + MMModemGsmNetworkRegStatus status, + const char *oper_code, + const char *oper_name, + GError *error, + gpointer user_data) +{ + MMCallbackInfo *info = (MMCallbackInfo *) user_data; + GHashTable *properties; + + if (error) + info->error = g_error_copy (error); + else { + properties = (GHashTable *) mm_callback_info_get_data (info, "simple-get-status"); + + g_hash_table_insert (properties, "registration_status", simple_uint_value (status)); + g_hash_table_insert (properties, "operator_code", simple_string_value (oper_code)); + g_hash_table_insert (properties, "operator_name", simple_string_value (oper_name)); + } + + mm_callback_info_schedule (info); +} + +static void +simple_get_status_invoke (MMCallbackInfo *info) +{ + MMModemSimpleGetStatusFn callback = (MMModemSimpleGetStatusFn) info->callback; + + callback (MM_MODEM_SIMPLE (info->modem), + (GHashTable *) mm_callback_info_get_data (info, "simple-get-status"), + info->error, info->user_data); +} + +static void +simple_get_status (MMModemSimple *simple, + MMModemSimpleGetStatusFn callback, + gpointer user_data) +{ + MMModemGsmNetwork *gsm; + GHashTable *properties; + MMCallbackInfo *info; + + info = mm_callback_info_new_full (MM_MODEM (simple), + simple_get_status_invoke, + G_CALLBACK (callback), + user_data); + + properties = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, simple_free_gvalue); + mm_callback_info_set_data (info, "simple-get-status", properties, (GDestroyNotify) g_hash_table_unref); + + gsm = MM_MODEM_GSM_NETWORK (simple); + mm_modem_gsm_network_get_signal_quality (gsm, simple_status_got_signal_quality, properties); + mm_modem_gsm_network_get_band (gsm, simple_status_got_band, properties); + mm_modem_gsm_network_get_mode (gsm, simple_status_got_mode, properties); + mm_modem_gsm_network_get_registration_info (gsm, simple_status_got_reg_info, properties); +} + +/*****************************************************************************/ + +static void +modem_init (MMModem *modem_class) +{ + modem_class->owns_port = owns_port; + modem_class->grab_port = grab_port; + modem_class->release_port = release_port; + modem_class->enable = enable; + modem_class->disable = disable; + modem_class->connect = connect; + modem_class->disconnect = disconnect; + modem_class->get_info = get_card_info; +} + +static void +modem_gsm_card_init (MMModemGsmCard *class) +{ + class->get_imei = get_imei; + class->get_imsi = get_imsi; + class->send_pin = send_pin; + class->send_puk = send_puk; + class->enable_pin = enable_pin; + class->change_pin = change_pin; +} + +static void +modem_gsm_network_init (MMModemGsmNetwork *class) +{ + class->do_register = do_register; + class->get_registration_info = get_registration_info; + class->set_apn = set_apn; + class->scan = scan; + class->get_signal_quality = get_signal_quality; +} + +static void +modem_gsm_sms_init (MMModemGsmSms *class) +{ + class->send = sms_send; +} + +static void +modem_simple_init (MMModemSimple *class) +{ + class->connect = simple_connect; + class->get_status = simple_get_status; +} + +static void +mm_generic_gsm_init (MMGenericGsm *self) +{ +} + +static void +set_property (GObject *object, guint prop_id, + const GValue *value, GParamSpec *pspec) +{ + switch (prop_id) { + case MM_MODEM_PROP_TYPE: + case MM_GENERIC_GSM_PROP_POWER_UP_CMD: + case MM_GENERIC_GSM_PROP_POWER_DOWN_CMD: + case MM_GENERIC_GSM_PROP_INIT_CMD: + case MM_GENERIC_GSM_PROP_INIT_CMD_OPTIONAL: + case MM_GENERIC_GSM_PROP_SUPPORTED_BANDS: + case MM_GENERIC_GSM_PROP_SUPPORTED_MODES: + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +get_property (GObject *object, guint prop_id, + GValue *value, GParamSpec *pspec) +{ + MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (object); + + switch (prop_id) { + case MM_MODEM_PROP_DATA_DEVICE: + if (priv->data) + g_value_set_string (value, mm_port_get_device (priv->data)); + else + g_value_set_string (value, NULL); + break; + case MM_MODEM_PROP_TYPE: + g_value_set_uint (value, MM_MODEM_TYPE_GSM); + break; + case MM_GENERIC_GSM_PROP_POWER_UP_CMD: + g_value_set_string (value, "+CFUN=1"); + break; + case MM_GENERIC_GSM_PROP_POWER_DOWN_CMD: + /* CFUN=0 is dangerous and often will shoot devices in the head (that's + * what it's supposed to do). So don't use CFUN=0 by default, but let + * specific plugins use it when they know it's safe to do so. For + * example, CFUN=0 will often make phones turn themselves off, but some + * dedicated devices (ex Sierra WWAN cards) will just turn off their + * radio but otherwise still work. + */ + g_value_set_string (value, ""); + break; + case MM_GENERIC_GSM_PROP_INIT_CMD: + g_value_set_string (value, "Z E0 V1 +CMEE=1"); + break; + case MM_GENERIC_GSM_PROP_INIT_CMD_OPTIONAL: + g_value_set_string (value, "X4 &C1"); + break; + case MM_GENERIC_GSM_PROP_SUPPORTED_BANDS: + g_value_set_uint (value, 0); + break; + case MM_GENERIC_GSM_PROP_SUPPORTED_MODES: + g_value_set_uint (value, 0); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +finalize (GObject *object) +{ + MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (object); + + mm_generic_gsm_pending_registration_stop (MM_GENERIC_GSM (object)); + + g_free (priv->oper_code); + g_free (priv->oper_name); + + G_OBJECT_CLASS (mm_generic_gsm_parent_class)->finalize (object); +} + +static void +mm_generic_gsm_class_init (MMGenericGsmClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + mm_generic_gsm_parent_class = g_type_class_peek_parent (klass); + g_type_class_add_private (object_class, sizeof (MMGenericGsmPrivate)); + + /* Virtual methods */ + object_class->set_property = set_property; + object_class->get_property = get_property; + object_class->finalize = finalize; + + klass->do_enable = real_do_enable; + + /* Properties */ + g_object_class_override_property (object_class, + MM_MODEM_PROP_DATA_DEVICE, + MM_MODEM_DATA_DEVICE); + + g_object_class_override_property (object_class, + MM_MODEM_PROP_TYPE, + MM_MODEM_TYPE); + + g_object_class_override_property (object_class, + MM_GENERIC_GSM_PROP_SUPPORTED_BANDS, + MM_MODEM_GSM_CARD_SUPPORTED_BANDS); + + g_object_class_override_property (object_class, + MM_GENERIC_GSM_PROP_SUPPORTED_MODES, + MM_MODEM_GSM_CARD_SUPPORTED_MODES); + + g_object_class_install_property + (object_class, MM_GENERIC_GSM_PROP_POWER_UP_CMD, + g_param_spec_string (MM_GENERIC_GSM_POWER_UP_CMD, + "PowerUpCommand", + "Power up command", + "+CFUN=1", + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); + + g_object_class_install_property + (object_class, MM_GENERIC_GSM_PROP_POWER_DOWN_CMD, + g_param_spec_string (MM_GENERIC_GSM_POWER_DOWN_CMD, + "PowerDownCommand", + "Power down command", + "+CFUN=0", + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); + + g_object_class_install_property + (object_class, MM_GENERIC_GSM_PROP_INIT_CMD, + g_param_spec_string (MM_GENERIC_GSM_INIT_CMD, + "InitCommand", + "Initialization command", + NULL, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); + + g_object_class_install_property + (object_class, MM_GENERIC_GSM_PROP_INIT_CMD_OPTIONAL, + g_param_spec_string (MM_GENERIC_GSM_INIT_CMD_OPTIONAL, + "InitCommandOptional", + "Optional initialization command (errors ignored)", + NULL, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); +} + diff --git a/src/mm-generic-gsm.h b/src/mm-generic-gsm.h new file mode 100644 index 0000000..de0b00b --- /dev/null +++ b/src/mm-generic-gsm.h @@ -0,0 +1,129 @@ +/* -*- 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_GENERIC_GSM_H +#define MM_GENERIC_GSM_H + +#include "mm-modem-gsm.h" +#include "mm-modem-gsm-network.h" +#include "mm-modem-base.h" +#include "mm-serial-port.h" +#include "mm-callback-info.h" + +#define MM_TYPE_GENERIC_GSM (mm_generic_gsm_get_type ()) +#define MM_GENERIC_GSM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_GENERIC_GSM, MMGenericGsm)) +#define MM_GENERIC_GSM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_GENERIC_GSM, MMGenericGsmClass)) +#define MM_IS_GENERIC_GSM(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_GENERIC_GSM)) +#define MM_IS_GENERIC_GSM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_GENERIC_GSM)) +#define MM_GENERIC_GSM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_GENERIC_GSM, MMGenericGsmClass)) + +#define MM_GENERIC_GSM_POWER_UP_CMD "power-up-cmd" +#define MM_GENERIC_GSM_POWER_DOWN_CMD "power-down-cmd" +#define MM_GENERIC_GSM_INIT_CMD "init-cmd" +#define MM_GENERIC_GSM_INIT_CMD_OPTIONAL "init-cmd-optional" + +typedef enum { + MM_GENERIC_GSM_PROP_FIRST = 0x2000, + + MM_GENERIC_GSM_PROP_POWER_UP_CMD, + MM_GENERIC_GSM_PROP_POWER_DOWN_CMD, + MM_GENERIC_GSM_PROP_INIT_CMD, + MM_GENERIC_GSM_PROP_SUPPORTED_BANDS, + MM_GENERIC_GSM_PROP_SUPPORTED_MODES, + MM_GENERIC_GSM_PROP_INIT_CMD_OPTIONAL +} MMGenericGsmProp; + + +typedef struct { + MMModemBase parent; +} MMGenericGsm; + +typedef struct { + MMModemBaseClass parent; + + /* Called after opening the primary serial port and updating the modem's + * state to ENABLING, but before sending any commands to the device. Modems + * that need to perform custom initialization sequences or other setup should + * generally override this method instead of the MMModem interface's enable() + * method, unless the customization must happen *after* the generic init + * sequence has completed. + */ + void (*do_enable) (MMGenericGsm *self, MMModemFn callback, gpointer user_data); +} MMGenericGsmClass; + +GType mm_generic_gsm_get_type (void); + +MMModem *mm_generic_gsm_new (const char *device, + const char *driver, + const char *plugin); + +/* Private, for subclasses */ + +#define MM_GENERIC_GSM_PREV_STATE_TAG "prev-state" + +void mm_generic_gsm_set_unsolicited_registration (MMGenericGsm *modem, + gboolean enabled); + +void mm_generic_gsm_pending_registration_stop (MMGenericGsm *modem); + +void mm_generic_gsm_set_cid (MMGenericGsm *modem, + guint32 cid); + +guint32 mm_generic_gsm_get_cid (MMGenericGsm *modem); +void mm_generic_gsm_set_reg_status (MMGenericGsm *modem, + MMModemGsmNetworkRegStatus status); + +void mm_generic_gsm_check_pin (MMGenericGsm *modem, + MMModemFn callback, + gpointer user_data); + +MMSerialPort *mm_generic_gsm_get_port (MMGenericGsm *modem, + MMPortType ptype); + +MMPort *mm_generic_gsm_grab_port (MMGenericGsm *modem, + const char *subsys, + const char *name, + MMPortType ptype, + GError **error); + +/* stay_connected should be TRUE for unsolicited registration updates, otherwise + * the registration update will clear connected/connecting/disconnecting state + * which we don't want. stay_connected should be FALSE for other cases like + * updating the state after disconnecting, or after a connect error occurs. + */ +void mm_generic_gsm_update_enabled_state (MMGenericGsm *modem, + gboolean stay_connected, + MMModemStateReason reason); + +/* Called to complete the enable operation for custom enable() handling; if an + * error is passed in, it copies the error to the callback info. This function + * always schedules the callback info. It will also update the modem with the + * correct state for both failure and success of the enable operation. + */ +void mm_generic_gsm_enable_complete (MMGenericGsm *modem, + GError *error, + MMCallbackInfo *info); + +/* Called to complete the enable operation for custom connect() handling; if an + * error is passed in, it copies the error to the callback info. This function + * always schedules the callback info. It will also update the modem with the + * correct state for both failure and success of the connect operation. + */ +void mm_generic_gsm_connect_complete (MMGenericGsm *modem, + GError *error, + MMCallbackInfo *info); + +#endif /* MM_GENERIC_GSM_H */ diff --git a/src/mm-manager.c b/src/mm-manager.c new file mode 100644 index 0000000..1a93170 --- /dev/null +++ b/src/mm-manager.c @@ -0,0 +1,714 @@ +/* -*- 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. + */ + +#include <string.h> +#include <gmodule.h> +#define G_UDEV_API_IS_SUBJECT_TO_CHANGE +#include <gudev/gudev.h> +#include <dbus/dbus-glib.h> +#include <dbus/dbus-glib-lowlevel.h> +#include "mm-manager.h" +#include "mm-errors.h" +#include "mm-plugin.h" + +static gboolean impl_manager_enumerate_devices (MMManager *manager, + GPtrArray **devices, + GError **err); + +#include "mm-manager-glue.h" + +G_DEFINE_TYPE (MMManager, mm_manager, G_TYPE_OBJECT) + +enum { + DEVICE_ADDED, + DEVICE_REMOVED, + + LAST_SIGNAL +}; + +static guint signals[LAST_SIGNAL] = { 0 }; + +#define MM_MANAGER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), MM_TYPE_MANAGER, MMManagerPrivate)) + +typedef struct { + DBusGConnection *connection; + GUdevClient *udev; + GSList *plugins; + GHashTable *modems; + + GHashTable *supports; +} MMManagerPrivate; + +static MMPlugin * +load_plugin (const char *path) +{ + MMPlugin *plugin = NULL; + GModule *module; + MMPluginCreateFunc plugin_create_func; + int *major_plugin_version, *minor_plugin_version; + + module = g_module_open (path, G_MODULE_BIND_LAZY); + if (!module) { + g_warning ("Could not load plugin %s: %s", path, g_module_error ()); + return NULL; + } + + if (!g_module_symbol (module, "mm_plugin_major_version", (gpointer *) &major_plugin_version)) { + g_warning ("Could not load plugin %s: Missing major version info", path); + goto out; + } + + if (*major_plugin_version != MM_PLUGIN_MAJOR_VERSION) { + g_warning ("Could not load plugin %s: Plugin major version %d, %d is required", + path, *major_plugin_version, MM_PLUGIN_MAJOR_VERSION); + goto out; + } + + if (!g_module_symbol (module, "mm_plugin_minor_version", (gpointer *) &minor_plugin_version)) { + g_warning ("Could not load plugin %s: Missing minor version info", path); + goto out; + } + + if (*minor_plugin_version != MM_PLUGIN_MINOR_VERSION) { + g_warning ("Could not load plugin %s: Plugin minor version %d, %d is required", + path, *minor_plugin_version, MM_PLUGIN_MINOR_VERSION); + goto out; + } + + if (!g_module_symbol (module, "mm_plugin_create", (gpointer *) &plugin_create_func)) { + g_warning ("Could not load plugin %s: %s", path, g_module_error ()); + goto out; + } + + plugin = (*plugin_create_func) (); + if (plugin) { + g_object_weak_ref (G_OBJECT (plugin), (GWeakNotify) g_module_close, module); + g_message ("Loaded plugin %s", mm_plugin_get_name (plugin)); + } else + g_warning ("Could not load plugin %s: initialization failed", path); + + out: + if (!plugin) + g_module_close (module); + + return plugin; +} + +static void +load_plugins (MMManager *manager) +{ + MMManagerPrivate *priv = MM_MANAGER_GET_PRIVATE (manager); + GDir *dir; + const char *fname; + MMPlugin *generic_plugin = NULL; + + if (!g_module_supported ()) { + g_warning ("GModules are not supported on your platform!"); + return; + } + + dir = g_dir_open (PLUGINDIR, 0, NULL); + if (!dir) { + g_warning ("No plugins found"); + return; + } + + while ((fname = g_dir_read_name (dir)) != NULL) { + char *path; + MMPlugin *plugin; + + if (!g_str_has_suffix (fname, G_MODULE_SUFFIX)) + continue; + + path = g_module_build_path (PLUGINDIR, fname); + plugin = load_plugin (path); + g_free (path); + + if (plugin) { + if (!strcmp (mm_plugin_get_name (plugin), MM_PLUGIN_GENERIC_NAME)) + generic_plugin = plugin; + else + priv->plugins = g_slist_append (priv->plugins, plugin); + } + } + + /* Make sure the generic plugin is last */ + if (generic_plugin) + priv->plugins = g_slist_append (priv->plugins, generic_plugin); + + g_dir_close (dir); +} + +MMManager * +mm_manager_new (DBusGConnection *bus) +{ + MMManager *manager; + + g_return_val_if_fail (bus != NULL, NULL); + + manager = (MMManager *) g_object_new (MM_TYPE_MANAGER, NULL); + if (manager) { + MMManagerPrivate *priv = MM_MANAGER_GET_PRIVATE (manager); + + priv->connection = dbus_g_connection_ref (bus); + dbus_g_connection_register_g_object (priv->connection, + MM_DBUS_PATH, + G_OBJECT (manager)); + } + + return manager; +} + +static void +remove_modem (MMManager *manager, MMModem *modem) +{ + MMManagerPrivate *priv = MM_MANAGER_GET_PRIVATE (manager); + char *device; + + device = mm_modem_get_device (modem); + g_assert (device); + g_debug ("Removed modem %s", device); + + g_signal_emit (manager, signals[DEVICE_REMOVED], 0, modem); + g_hash_table_remove (priv->modems, device); + g_free (device); +} + +static void +modem_valid (MMModem *modem, GParamSpec *pspec, gpointer user_data) +{ + MMManager *manager = MM_MANAGER (user_data); + MMManagerPrivate *priv = MM_MANAGER_GET_PRIVATE (manager); + static guint32 id = 0; + char *path, *device; + + if (mm_modem_get_valid (modem)) { + path = g_strdup_printf (MM_DBUS_PATH"/Modems/%d", id++); + dbus_g_connection_register_g_object (priv->connection, path, G_OBJECT (modem)); + g_object_set_data_full (G_OBJECT (modem), DBUS_PATH_TAG, path, (GDestroyNotify) g_free); + + device = mm_modem_get_device (modem); + g_assert (device); + g_debug ("Exported modem %s as %s", device, path); + g_free (device); + + g_signal_emit (manager, signals[DEVICE_ADDED], 0, modem); + } else + remove_modem (manager, modem); +} + +static void +add_modem (MMManager *manager, MMModem *modem) +{ + MMManagerPrivate *priv = MM_MANAGER_GET_PRIVATE (manager); + char *device; + gboolean valid = FALSE; + + device = mm_modem_get_device (modem); + g_assert (device); + if (!g_hash_table_lookup (priv->modems, device)) { + g_hash_table_insert (priv->modems, g_strdup (device), modem); + g_debug ("Added modem %s", device); + g_signal_connect (modem, "notify::valid", G_CALLBACK (modem_valid), manager); + g_object_get (modem, MM_MODEM_VALID, &valid, NULL); + if (valid) + modem_valid (modem, NULL, manager); + } + g_free (device); +} + +static void +enumerate_devices_cb (gpointer key, gpointer val, gpointer user_data) +{ + MMModem *modem = MM_MODEM (val); + GPtrArray **devices = (GPtrArray **) user_data; + const char *path; + gboolean valid = FALSE; + + g_object_get (G_OBJECT (modem), MM_MODEM_VALID, &valid, NULL); + if (valid) { + path = g_object_get_data (G_OBJECT (modem), DBUS_PATH_TAG); + g_return_if_fail (path != NULL); + g_ptr_array_add (*devices, g_strdup (path)); + } +} + +static gboolean +impl_manager_enumerate_devices (MMManager *manager, + GPtrArray **devices, + GError **err) +{ + MMManagerPrivate *priv = MM_MANAGER_GET_PRIVATE (manager); + + *devices = g_ptr_array_sized_new (g_hash_table_size (priv->modems)); + g_hash_table_foreach (priv->modems, enumerate_devices_cb, devices); + + return TRUE; +} + +static MMModem * +find_modem_for_device (MMManager *manager, const char *device) +{ + MMManagerPrivate *priv = MM_MANAGER_GET_PRIVATE (manager); + GHashTableIter iter; + gpointer key, value; + + g_hash_table_iter_init (&iter, priv->modems); + while (g_hash_table_iter_next (&iter, &key, &value)) { + MMModem *modem = MM_MODEM (value); + + if (!strcmp (device, mm_modem_get_device (modem))) + return modem; + } + return NULL; +} + + +static MMModem * +find_modem_for_port (MMManager *manager, const char *subsys, const char *name) +{ + MMManagerPrivate *priv = MM_MANAGER_GET_PRIVATE (manager); + GHashTableIter iter; + gpointer key, value; + + g_hash_table_iter_init (&iter, priv->modems); + while (g_hash_table_iter_next (&iter, &key, &value)) { + MMModem *modem = MM_MODEM (value); + + if (mm_modem_owns_port (modem, subsys, name)) + return modem; + } + return NULL; +} + +typedef struct { + MMManager *manager; + char *subsys; + char *name; + GSList *plugins; + GSList *cur_plugin; + guint defer_id; + guint done_id; + + guint32 best_level; + MMPlugin *best_plugin; +} SupportsInfo; + +static SupportsInfo * +supports_info_new (MMManager *self, const char *subsys, const char *name) +{ + MMManagerPrivate *priv = MM_MANAGER_GET_PRIVATE (self); + SupportsInfo *info; + + info = g_malloc0 (sizeof (SupportsInfo)); + info->manager = self; + info->subsys = g_strdup (subsys); + info->name = g_strdup (name); + info->plugins = g_slist_copy (priv->plugins); + info->cur_plugin = info->plugins; + return info; +} + +static void +supports_info_free (SupportsInfo *info) +{ + /* Cancel any in-process operation on the first plugin */ + if (info->cur_plugin) + mm_plugin_cancel_supports_port (MM_PLUGIN (info->cur_plugin->data), info->subsys, info->name); + + if (info->defer_id) + g_source_remove (info->defer_id); + + if (info->done_id) + g_source_remove (info->done_id); + + g_free (info->subsys); + g_free (info->name); + g_slist_free (info->plugins); + memset (info, 0, sizeof (SupportsInfo)); + g_free (info); +} + +static char * +get_key (const char *subsys, const char *name) +{ + return g_strdup_printf ("%s%s", subsys, name); +} + + +static void supports_callback (MMPlugin *plugin, + const char *subsys, + const char *name, + guint32 level, + gpointer user_data); + +static void try_supports_port (MMManager *manager, + MMPlugin *plugin, + const char *subsys, + const char *name, + SupportsInfo *info); + +static gboolean +supports_defer_timeout (gpointer user_data) +{ + SupportsInfo *info = user_data; + + g_debug ("(%s): re-checking support...", info->name); + try_supports_port (info->manager, + MM_PLUGIN (info->cur_plugin->data), + info->subsys, + info->name, + info); + return FALSE; +} + +static void +try_supports_port (MMManager *manager, + MMPlugin *plugin, + const char *subsys, + const char *name, + SupportsInfo *info) +{ + MMPluginSupportsResult result; + + result = mm_plugin_supports_port (plugin, subsys, name, supports_callback, info); + + switch (result) { + case MM_PLUGIN_SUPPORTS_PORT_UNSUPPORTED: + /* If the plugin knows it doesn't support the modem, just call the + * callback and indicate 0 support. + */ + supports_callback (plugin, subsys, name, 0, info); + break; + case MM_PLUGIN_SUPPORTS_PORT_DEFER: + g_debug ("(%s): (%s) deferring support check", mm_plugin_get_name (plugin), name); + if (info->defer_id) + g_source_remove (info->defer_id); + + /* defer port detection for a bit as requested by the plugin */ + info->defer_id = g_timeout_add (3000, supports_defer_timeout, info); + break; + case MM_PLUGIN_SUPPORTS_PORT_IN_PROGRESS: + default: + break; + } +} + +static gboolean +do_grab_port (gpointer user_data) +{ + SupportsInfo *info = user_data; + MMManagerPrivate *priv = MM_MANAGER_GET_PRIVATE (info->manager); + MMModem *modem; + GError *error = NULL; + char *key; + GSList *iter; + + /* No more plugins to try */ + if (info->best_plugin) { + /* Create the modem */ + modem = mm_plugin_grab_port (info->best_plugin, info->subsys, info->name, &error); + if (modem) { + guint32 modem_type = MM_MODEM_TYPE_UNKNOWN; + const char *type_name = "UNKNOWN"; + + g_object_get (G_OBJECT (modem), MM_MODEM_TYPE, &modem_type, NULL); + if (modem_type == MM_MODEM_TYPE_GSM) + type_name = "GSM"; + else if (modem_type == MM_MODEM_TYPE_CDMA) + type_name = "CDMA"; + + g_message ("(%s): %s modem %s claimed port %s", + mm_plugin_get_name (info->best_plugin), + type_name, + mm_modem_get_device (modem), + info->name); + + add_modem (info->manager, modem); + } else { + g_warning ("%s: plugin '%s' claimed to support %s/%s but couldn't: (%d) %s", + __func__, + mm_plugin_get_name (info->best_plugin), + info->subsys, + info->name, + error ? error->code : -1, + (error && error->message) ? error->message : "(unknown)"); + } + } + + /* Tell each plugin to clean up any outstanding supports task */ + for (iter = info->plugins; iter; iter = g_slist_next (iter)) + mm_plugin_cancel_supports_port (MM_PLUGIN (iter->data), info->subsys, info->name); + g_slist_free (info->plugins); + info->cur_plugin = info->plugins = NULL; + + key = get_key (info->subsys, info->name); + g_hash_table_remove (priv->supports, key); + g_free (key); + + return FALSE; +} + +static void +supports_callback (MMPlugin *plugin, + const char *subsys, + const char *name, + guint32 level, + gpointer user_data) +{ + SupportsInfo *info = user_data; + MMPlugin *next_plugin = NULL; + + info->cur_plugin = info->cur_plugin->next; + if (info->cur_plugin) + next_plugin = MM_PLUGIN (info->cur_plugin->data); + + /* Is this plugin's result better than any one we've tried before? */ + if (level > info->best_level) { + info->best_level = level; + info->best_plugin = plugin; + } + + /* Prevent the generic plugin from probing devices that are already supported + * by other plugins. For Huawei for example, secondary ports shouldn't + * be probed, but the generic plugin would happily do so if allowed to. + */ + if ( next_plugin + && !strcmp (mm_plugin_get_name (next_plugin), MM_PLUGIN_GENERIC_NAME) + && info->best_plugin) + next_plugin = NULL; + + if (next_plugin) { + /* Try the next plugin */ + try_supports_port (info->manager, next_plugin, info->subsys, info->name, info); + } else { + /* All done; let the best modem grab the port */ + info->done_id = g_idle_add (do_grab_port, info); + } +} + +static void +device_added (MMManager *manager, GUdevDevice *device) +{ + MMManagerPrivate *priv = MM_MANAGER_GET_PRIVATE (manager); + const char *subsys, *name; + SupportsInfo *info; + char *key; + gboolean found; + + g_return_if_fail (device != NULL); + + if (!g_slist_length (priv->plugins)) + return; + + subsys = g_udev_device_get_subsystem (device); + name = g_udev_device_get_name (device); + + if (find_modem_for_port (manager, subsys, name)) + return; + + key = get_key (subsys, name); + found = !!g_hash_table_lookup (priv->supports, key); + if (found) { + g_free (key); + return; + } + + info = supports_info_new (manager, subsys, name); + g_hash_table_insert (priv->supports, key, info); + + try_supports_port (manager, MM_PLUGIN (info->cur_plugin->data), subsys, name, info); +} + +static void +device_removed (MMManager *manager, GUdevDevice *device) +{ + MMManagerPrivate *priv = MM_MANAGER_GET_PRIVATE (manager); + MMModem *modem; + const char *subsys, *name; + char *key; + SupportsInfo *info; + + g_return_if_fail (device != NULL); + + if (!g_slist_length (priv->plugins)) + return; + + subsys = g_udev_device_get_subsystem (device); + name = g_udev_device_get_name (device); + + if (strcmp (subsys, "usb") != 0) { + /* find_modem_for_port handles tty and net removal */ + modem = find_modem_for_port (manager, subsys, name); + if (modem) { + mm_modem_release_port (modem, subsys, name); + return; + } + } else { + /* This case is designed to handle the case where, at least with kernel 2.6.31, unplugging + * an in-use ttyACMx device results in udev generating remove events for the usb, but the + * ttyACMx device (subsystem tty) is not removed, since it was in-use. So if we have not + * found a modem for the port (above), we're going to look here to see if we have a modem + * associated with the newly removed device. If so, we'll remove the modem, since the + * device has been removed. That way, if the device is reinserted later, we'll go through + * the process of exporting it. + */ + const char *sysfs_path = g_udev_device_get_sysfs_path (device); + + // g_debug ("Looking for a modem for removed device %s", sysfs_path); + modem = find_modem_for_device (manager, sysfs_path); + if (modem) { + g_debug ("Removing modem claimed by removed device %s", sysfs_path); + remove_modem (manager, modem); + return; + } + } + + /* Maybe a plugin is checking whether or not the port is supported */ + key = get_key (subsys, name); + info = g_hash_table_lookup (priv->supports, key); + + if (info) { + if (info->plugins) + mm_plugin_cancel_supports_port (MM_PLUGIN (info->plugins->data), subsys, name); + g_hash_table_remove (priv->supports, key); + } + + g_free (key); +} + +static void +handle_uevent (GUdevClient *client, + const char *action, + GUdevDevice *device, + gpointer user_data) +{ + MMManager *self = MM_MANAGER (user_data); + const char *subsys; + + g_return_if_fail (action != NULL); + + /* A bit paranoid */ + subsys = g_udev_device_get_subsystem (device); + g_return_if_fail (subsys != NULL); + + g_return_if_fail (!strcmp (subsys, "tty") || !strcmp (subsys, "net") || !strcmp (subsys, "usb")); + + /* We only care about tty/net devices when adding modem ports, + * but for remove, also handle usb parent device remove events + */ + if ((!strcmp (action, "add") || !strcmp (action, "move")) && strcmp (subsys, "usb") !=0 ) + device_added (self, device); + else if (!strcmp (action, "remove")) + device_removed (self, device); +} + +void +mm_manager_start (MMManager *manager) +{ + MMManagerPrivate *priv; + GList *devices, *iter; + + g_return_if_fail (manager != NULL); + g_return_if_fail (MM_IS_MANAGER (manager)); + + priv = MM_MANAGER_GET_PRIVATE (manager); + + devices = g_udev_client_query_by_subsystem (priv->udev, "tty"); + for (iter = devices; iter; iter = g_list_next (iter)) + device_added (manager, G_UDEV_DEVICE (iter->data)); + + devices = g_udev_client_query_by_subsystem (priv->udev, "net"); + for (iter = devices; iter; iter = g_list_next (iter)) + device_added (manager, G_UDEV_DEVICE (iter->data)); +} + +static void +mm_manager_init (MMManager *manager) +{ + MMManagerPrivate *priv = MM_MANAGER_GET_PRIVATE (manager); + const char *subsys[4] = { "tty", "net", "usb", NULL }; + + priv->modems = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref); + load_plugins (manager); + + priv->supports = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify) supports_info_free); + + priv->udev = g_udev_client_new (subsys); + g_assert (priv->udev); + g_signal_connect (priv->udev, "uevent", G_CALLBACK (handle_uevent), manager); +} + +static void +finalize (GObject *object) +{ + MMManagerPrivate *priv = MM_MANAGER_GET_PRIVATE (object); + + g_hash_table_destroy (priv->supports); + g_hash_table_destroy (priv->modems); + + g_slist_foreach (priv->plugins, (GFunc) g_object_unref, NULL); + g_slist_free (priv->plugins); + + if (priv->udev) + g_object_unref (priv->udev); + + if (priv->connection) + dbus_g_connection_unref (priv->connection); + + G_OBJECT_CLASS (mm_manager_parent_class)->finalize (object); +} + +static void +mm_manager_class_init (MMManagerClass *manager_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (manager_class); + + g_type_class_add_private (object_class, sizeof (MMManagerPrivate)); + + /* Virtual methods */ + object_class->finalize = finalize; + + /* Signals */ + signals[DEVICE_ADDED] = + g_signal_new ("device-added", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (MMManagerClass, device_added), + NULL, NULL, + g_cclosure_marshal_VOID__OBJECT, + G_TYPE_NONE, 1, + G_TYPE_OBJECT); + + signals[DEVICE_REMOVED] = + g_signal_new ("device-removed", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (MMManagerClass, device_removed), + NULL, NULL, + g_cclosure_marshal_VOID__OBJECT, + G_TYPE_NONE, 1, + G_TYPE_OBJECT); + + dbus_g_object_type_install_info (G_TYPE_FROM_CLASS (manager_class), + &dbus_glib_mm_manager_object_info); + + dbus_g_error_domain_register (MM_SERIAL_ERROR, "org.freedesktop.ModemManager.Modem", MM_TYPE_SERIAL_ERROR); + dbus_g_error_domain_register (MM_MODEM_ERROR, "org.freedesktop.ModemManager.Modem", MM_TYPE_MODEM_ERROR); + dbus_g_error_domain_register (MM_MODEM_CONNECT_ERROR, "org.freedesktop.ModemManager.Modem", MM_TYPE_MODEM_CONNECT_ERROR); + dbus_g_error_domain_register (MM_MOBILE_ERROR, "org.freedesktop.ModemManager.Modem.Gsm", MM_TYPE_MOBILE_ERROR); +} + diff --git a/src/mm-manager.h b/src/mm-manager.h new file mode 100644 index 0000000..5220b77 --- /dev/null +++ b/src/mm-manager.h @@ -0,0 +1,53 @@ +/* -*- 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_MANAGER_H +#define MM_MANAGER_H + +#include <glib/gtypes.h> +#include <glib-object.h> +#include <dbus/dbus-glib.h> +#include "mm-modem.h" + +#define MM_TYPE_MANAGER (mm_manager_get_type ()) +#define MM_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_MANAGER, MMManager)) +#define MM_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_MANAGER, MMManagerClass)) +#define MM_IS_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_MANAGER)) +#define MM_IS_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), MM_TYPE_MANAGER)) +#define MM_MANAGER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_MANAGER, MMManagerClass)) + +#define MM_DBUS_SERVICE "org.freedesktop.ModemManager" +#define MM_DBUS_PATH "/org/freedesktop/ModemManager" + +typedef struct { + GObject parent; +} MMManager; + +typedef struct { + GObjectClass parent; + + /* Signals */ + void (*device_added) (MMManager *manager, MMModem *device); + void (*device_removed) (MMManager *manager, MMModem *device); +} MMManagerClass; + +GType mm_manager_get_type (void); + +MMManager *mm_manager_new (DBusGConnection *bus); + +void mm_manager_start (MMManager *manager); + +#endif /* MM_MANAGER_H */ diff --git a/src/mm-modem-base.c b/src/mm-modem-base.c new file mode 100644 index 0000000..3d82f8e --- /dev/null +++ b/src/mm-modem-base.c @@ -0,0 +1,341 @@ +/* -*- 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. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> + +#include "mm-modem-base.h" +#include "mm-modem.h" +#include "mm-serial-port.h" +#include "mm-errors.h" +#include "mm-options.h" +#include "mm-properties-changed-signal.h" + +static void modem_init (MMModem *modem_class); + +G_DEFINE_TYPE_EXTENDED (MMModemBase, mm_modem_base, + G_TYPE_OBJECT, + G_TYPE_FLAG_VALUE_ABSTRACT, + G_IMPLEMENT_INTERFACE (MM_TYPE_MODEM, modem_init)) + +#define MM_MODEM_BASE_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), MM_TYPE_MODEM_BASE, MMModemBasePrivate)) + +typedef struct { + char *driver; + char *plugin; + char *device; + guint32 ip_method; + gboolean valid; + MMModemState state; + + GHashTable *ports; +} MMModemBasePrivate; + + +static char * +get_hash_key (const char *subsys, const char *name) +{ + return g_strdup_printf ("%s%s", subsys, name); +} + +MMPort * +mm_modem_base_get_port (MMModemBase *self, + const char *subsys, + const char *name) +{ + MMPort *port; + char *key; + + g_return_val_if_fail (self != NULL, NULL); + g_return_val_if_fail (MM_IS_MODEM_BASE (self), NULL); + g_return_val_if_fail (name != NULL, NULL); + g_return_val_if_fail (subsys != NULL, NULL); + + g_return_val_if_fail (!strcmp (subsys, "net") || !strcmp (subsys, "tty"), NULL); + + key = get_hash_key (subsys, name); + port = g_hash_table_lookup (MM_MODEM_BASE_GET_PRIVATE (self)->ports, key); + g_free (key); + return port; +} + +static void +find_primary (gpointer key, gpointer data, gpointer user_data) +{ + MMPort **found = user_data; + MMPort *port = MM_PORT (data); + + if (!*found && (mm_port_get_port_type (port) == MM_PORT_TYPE_PRIMARY)) + *found = port; +} + +MMPort * +mm_modem_base_add_port (MMModemBase *self, + const char *subsys, + const char *name, + MMPortType ptype) +{ + MMModemBasePrivate *priv = MM_MODEM_BASE_GET_PRIVATE (self); + MMPort *port = NULL; + char *key; + + g_return_val_if_fail (MM_IS_MODEM_BASE (self), NULL); + g_return_val_if_fail (subsys != NULL, NULL); + g_return_val_if_fail (name != NULL, NULL); + g_return_val_if_fail (ptype != MM_PORT_TYPE_UNKNOWN, NULL); + + g_return_val_if_fail (!strcmp (subsys, "net") || !strcmp (subsys, "tty"), NULL); + + key = get_hash_key (subsys, name); + port = g_hash_table_lookup (priv->ports, key); + g_free (key); + g_return_val_if_fail (port == NULL, NULL); + + if (ptype == MM_PORT_TYPE_PRIMARY) { + g_hash_table_foreach (priv->ports, find_primary, &port); + g_return_val_if_fail (port == NULL, FALSE); + } + + if (!strcmp (subsys, "tty")) + port = MM_PORT (mm_serial_port_new (name, ptype)); + else if (!strcmp (subsys, "net")) { + port = MM_PORT (g_object_new (MM_TYPE_PORT, + MM_PORT_DEVICE, name, + MM_PORT_SUBSYS, MM_PORT_SUBSYS_NET, + MM_PORT_TYPE, ptype, + NULL)); + } + + if (!port) + return NULL; + + key = get_hash_key (subsys, name); + g_hash_table_insert (priv->ports, key, port); + return port; +} + +gboolean +mm_modem_base_remove_port (MMModemBase *self, MMPort *port) +{ + g_return_val_if_fail (MM_IS_MODEM_BASE (self), FALSE); + g_return_val_if_fail (port != NULL, FALSE); + + return g_hash_table_remove (MM_MODEM_BASE_GET_PRIVATE (self)->ports, port); +} + +void +mm_modem_base_set_valid (MMModemBase *self, gboolean new_valid) +{ + MMModemBasePrivate *priv; + + g_return_if_fail (MM_IS_MODEM_BASE (self)); + + priv = MM_MODEM_BASE_GET_PRIVATE (self); + + if (priv->valid != new_valid) { + priv->valid = new_valid; + + /* Modem starts off in disabled state, and jumps to disabled when + * it's no longer valid. + */ + mm_modem_set_state (MM_MODEM (self), + MM_MODEM_STATE_DISABLED, + MM_MODEM_STATE_REASON_NONE); + + g_object_notify (G_OBJECT (self), MM_MODEM_VALID); + } +} + +gboolean +mm_modem_base_get_valid (MMModemBase *self) +{ + g_return_val_if_fail (MM_IS_MODEM_BASE (self), FALSE); + + return MM_MODEM_BASE_GET_PRIVATE (self)->valid; +} + +/*****************************************************************************/ + +static void +mm_modem_base_init (MMModemBase *self) +{ + MMModemBasePrivate *priv = MM_MODEM_BASE_GET_PRIVATE (self); + + priv->ports = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref); + + mm_properties_changed_signal_register_property (G_OBJECT (self), + MM_MODEM_ENABLED, + MM_MODEM_DBUS_INTERFACE); +} + +static void +modem_init (MMModem *modem_class) +{ +} + +static gboolean +is_enabled (MMModemState state) +{ + return (state >= MM_MODEM_STATE_ENABLED); +} + +static void +set_property (GObject *object, guint prop_id, + const GValue *value, GParamSpec *pspec) +{ + MMModemBasePrivate *priv = MM_MODEM_BASE_GET_PRIVATE (object); + gboolean old_enabled; + + switch (prop_id) { + case MM_MODEM_PROP_STATE: + /* Ensure we update the 'enabled' property when the state changes */ + old_enabled = is_enabled (priv->state); + priv->state = g_value_get_uint (value); + if (old_enabled != is_enabled (priv->state)) + g_object_notify (object, MM_MODEM_ENABLED); + break; + case MM_MODEM_PROP_DRIVER: + /* Construct only */ + priv->driver = g_value_dup_string (value); + break; + case MM_MODEM_PROP_PLUGIN: + /* Construct only */ + priv->plugin = g_value_dup_string (value); + break; + case MM_MODEM_PROP_MASTER_DEVICE: + /* Construct only */ + priv->device = g_value_dup_string (value); + break; + case MM_MODEM_PROP_IP_METHOD: + priv->ip_method = g_value_get_uint (value); + break; + case MM_MODEM_PROP_VALID: + case MM_MODEM_PROP_TYPE: + case MM_MODEM_PROP_ENABLED: + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +get_property (GObject *object, guint prop_id, + GValue *value, GParamSpec *pspec) +{ + MMModemBasePrivate *priv = MM_MODEM_BASE_GET_PRIVATE (object); + + switch (prop_id) { + case MM_MODEM_PROP_STATE: + g_value_set_uint (value, priv->state); + break; + case MM_MODEM_PROP_MASTER_DEVICE: + g_value_set_string (value, priv->device); + break; + case MM_MODEM_PROP_DATA_DEVICE: + g_value_set_string (value, NULL); + break; + case MM_MODEM_PROP_DRIVER: + g_value_set_string (value, priv->driver); + break; + case MM_MODEM_PROP_PLUGIN: + g_value_set_string (value, priv->plugin); + break; + case MM_MODEM_PROP_TYPE: + g_value_set_uint (value, MM_MODEM_TYPE_UNKNOWN); + break; + case MM_MODEM_PROP_IP_METHOD: + g_value_set_uint (value, priv->ip_method); + break; + case MM_MODEM_PROP_VALID: + g_value_set_boolean (value, priv->valid); + break; + case MM_MODEM_PROP_ENABLED: + g_value_set_boolean (value, is_enabled (priv->state)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +finalize (GObject *object) +{ + MMModemBase *self = MM_MODEM_BASE (object); + MMModemBasePrivate *priv = MM_MODEM_BASE_GET_PRIVATE (self); + + g_hash_table_destroy (priv->ports); + g_free (priv->driver); + g_free (priv->plugin); + g_free (priv->device); + + G_OBJECT_CLASS (mm_modem_base_parent_class)->finalize (object); +} + +static void +mm_modem_base_class_init (MMModemBaseClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + g_type_class_add_private (object_class, sizeof (MMModemBasePrivate)); + + /* Virtual methods */ + object_class->get_property = get_property; + object_class->set_property = set_property; + object_class->finalize = finalize; + + g_object_class_override_property (object_class, + MM_MODEM_PROP_STATE, + MM_MODEM_STATE); + + g_object_class_override_property (object_class, + MM_MODEM_PROP_MASTER_DEVICE, + MM_MODEM_MASTER_DEVICE); + + g_object_class_override_property (object_class, + MM_MODEM_PROP_DATA_DEVICE, + MM_MODEM_DATA_DEVICE); + + g_object_class_override_property (object_class, + MM_MODEM_PROP_DRIVER, + MM_MODEM_DRIVER); + + g_object_class_override_property (object_class, + MM_MODEM_PROP_PLUGIN, + MM_MODEM_PLUGIN); + + g_object_class_override_property (object_class, + MM_MODEM_PROP_TYPE, + MM_MODEM_TYPE); + + g_object_class_override_property (object_class, + MM_MODEM_PROP_IP_METHOD, + MM_MODEM_IP_METHOD); + + g_object_class_override_property (object_class, + MM_MODEM_PROP_VALID, + MM_MODEM_VALID); + + g_object_class_override_property (object_class, + MM_MODEM_PROP_ENABLED, + MM_MODEM_ENABLED); + + mm_properties_changed_signal_new (object_class); +} + diff --git a/src/mm-modem-base.h b/src/mm-modem-base.h new file mode 100644 index 0000000..9eb64ce --- /dev/null +++ b/src/mm-modem-base.h @@ -0,0 +1,64 @@ +/* -*- 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_BASE_H +#define MM_MODEM_BASE_H + +#include <glib.h> +#include <glib/gtypes.h> +#include <glib-object.h> + +#include "mm-port.h" + +#define MM_TYPE_MODEM_BASE (mm_modem_base_get_type ()) +#define MM_MODEM_BASE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_MODEM_BASE, MMModemBase)) +#define MM_MODEM_BASE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_MODEM_BASE, MMModemBaseClass)) +#define MM_IS_MODEM_BASE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_MODEM_BASE)) +#define MM_IS_MODEM_BASE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_MODEM_BASE)) +#define MM_MODEM_BASE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_MODEM_BASE, MMModemBaseClass)) + +typedef struct _MMModemBase MMModemBase; +typedef struct _MMModemBaseClass MMModemBaseClass; + +struct _MMModemBase { + GObject parent; +}; + +struct _MMModemBaseClass { + GObjectClass parent; +}; + +GType mm_modem_base_get_type (void); + +MMPort *mm_modem_base_get_port (MMModemBase *self, + const char *subsys, + const char *name); + +MMPort *mm_modem_base_add_port (MMModemBase *self, + const char *subsys, + const char *name, + MMPortType ptype); + +gboolean mm_modem_base_remove_port (MMModemBase *self, + MMPort *port); + +void mm_modem_base_set_valid (MMModemBase *self, + gboolean valid); + +gboolean mm_modem_base_get_valid (MMModemBase *self); + +#endif /* MM_MODEM_BASE_H */ + diff --git a/src/mm-modem-cdma.c b/src/mm-modem-cdma.c new file mode 100644 index 0000000..112b93f --- /dev/null +++ b/src/mm-modem-cdma.c @@ -0,0 +1,346 @@ +/* -*- 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 Novell, Inc. + * Copyright (C) 2009 Red Hat, Inc. + */ + +#include <string.h> +#include <dbus/dbus-glib.h> +#include "mm-modem-cdma.h" +#include "mm-errors.h" +#include "mm-callback-info.h" +#include "mm-marshal.h" + +static void impl_modem_cdma_get_signal_quality (MMModemCdma *modem, DBusGMethodInvocation *context); +static void impl_modem_cdma_get_esn (MMModemCdma *modem, DBusGMethodInvocation *context); +static void impl_modem_cdma_get_serving_system (MMModemCdma *modem, DBusGMethodInvocation *context); +static void impl_modem_cdma_get_registration_state (MMModemCdma *modem, DBusGMethodInvocation *context); + +#include "mm-modem-cdma-glue.h" + +enum { + SIGNAL_QUALITY, + REGISTRATION_STATE_CHANGED, + + LAST_SIGNAL +}; + +static guint signals[LAST_SIGNAL] = { 0 }; + +/*****************************************************************************/ + +static void +str_call_done (MMModem *modem, const char *result, GError *error, gpointer user_data) +{ + DBusGMethodInvocation *context = (DBusGMethodInvocation *) user_data; + + if (error) + dbus_g_method_return_error (context, error); + else + dbus_g_method_return (context, result); +} + +static void +str_call_not_supported (MMModemCdma *self, + MMModemStringFn callback, + gpointer user_data) +{ + MMCallbackInfo *info; + + info = mm_callback_info_string_new (MM_MODEM (self), callback, user_data); + info->error = g_error_new_literal (MM_MODEM_ERROR, MM_MODEM_ERROR_OPERATION_NOT_SUPPORTED, + "Operation not supported"); + + mm_callback_info_schedule (info); +} + +static void +uint_op_not_supported (MMModem *self, + MMModemUIntFn callback, + gpointer user_data) +{ + MMCallbackInfo *info; + + info = mm_callback_info_uint_new (self, callback, user_data); + info->error = g_error_new_literal (MM_MODEM_ERROR, MM_MODEM_ERROR_OPERATION_NOT_SUPPORTED, + "Operation not supported"); + mm_callback_info_schedule (info); +} + +static void +uint_call_done (MMModem *modem, guint32 result, GError *error, gpointer user_data) +{ + DBusGMethodInvocation *context = (DBusGMethodInvocation *) user_data; + + if (error) + dbus_g_method_return_error (context, error); + else + dbus_g_method_return (context, result); +} + +static void +serving_system_call_done (MMModemCdma *self, + guint32 class, + unsigned char band, + guint32 sid, + GError *error, + gpointer user_data) +{ + DBusGMethodInvocation *context = (DBusGMethodInvocation *) user_data; + + if (error) + dbus_g_method_return_error (context, error); + else { + GValueArray *array; + GValue value = { 0, }; + char band_str[2] = { 0, 0 }; + + array = g_value_array_new (3); + + /* Band Class */ + g_value_init (&value, G_TYPE_UINT); + g_value_set_uint (&value, class); + g_value_array_append (array, &value); + g_value_unset (&value); + + /* Band */ + g_value_init (&value, G_TYPE_STRING); + band_str[0] = band; + g_value_set_string (&value, band_str); + g_value_array_append (array, &value); + g_value_unset (&value); + + /* SID */ + g_value_init (&value, G_TYPE_UINT); + g_value_set_uint (&value, sid); + g_value_array_append (array, &value); + g_value_unset (&value); + + dbus_g_method_return (context, array); + } +} + +static void +serving_system_invoke (MMCallbackInfo *info) +{ + MMModemCdmaServingSystemFn callback = (MMModemCdmaServingSystemFn) info->callback; + + callback (MM_MODEM_CDMA (info->modem), 0, 0, 0, info->error, info->user_data); +} + +static void +serving_system_call_not_supported (MMModemCdma *self, + MMModemCdmaServingSystemFn callback, + gpointer user_data) +{ + MMCallbackInfo *info; + + info = mm_callback_info_new_full (MM_MODEM (self), serving_system_invoke, G_CALLBACK (callback), user_data); + info->error = g_error_new_literal (MM_MODEM_ERROR, MM_MODEM_ERROR_OPERATION_NOT_SUPPORTED, + "Operation not supported"); + + mm_callback_info_schedule (info); +} + +void +mm_modem_cdma_get_serving_system (MMModemCdma *self, + MMModemCdmaServingSystemFn callback, + gpointer user_data) +{ + g_return_if_fail (MM_IS_MODEM_CDMA (self)); + g_return_if_fail (callback != NULL); + + if (MM_MODEM_CDMA_GET_INTERFACE (self)->get_serving_system) + MM_MODEM_CDMA_GET_INTERFACE (self)->get_serving_system (self, callback, user_data); + else + serving_system_call_not_supported (self, callback, user_data); +} + +static void +impl_modem_cdma_get_serving_system (MMModemCdma *modem, + DBusGMethodInvocation *context) +{ + mm_modem_cdma_get_serving_system (modem, serving_system_call_done, context); +} + +void +mm_modem_cdma_get_esn (MMModemCdma *self, + MMModemStringFn callback, + gpointer user_data) +{ + g_return_if_fail (MM_IS_MODEM_CDMA (self)); + g_return_if_fail (callback != NULL); + + if (MM_MODEM_CDMA_GET_INTERFACE (self)->get_esn) + MM_MODEM_CDMA_GET_INTERFACE (self)->get_esn (self, callback, user_data); + else + str_call_not_supported (self, callback, user_data); +} + +static void +impl_modem_cdma_get_esn (MMModemCdma *modem, + DBusGMethodInvocation *context) +{ + mm_modem_cdma_get_esn (modem, str_call_done, context); +} + +void +mm_modem_cdma_get_signal_quality (MMModemCdma *self, + MMModemUIntFn callback, + gpointer user_data) +{ + g_return_if_fail (MM_IS_MODEM_CDMA (self)); + g_return_if_fail (callback != NULL); + + if (MM_MODEM_CDMA_GET_INTERFACE (self)->get_signal_quality) + MM_MODEM_CDMA_GET_INTERFACE (self)->get_signal_quality (self, callback, user_data); + else + uint_op_not_supported (MM_MODEM (self), callback, user_data); +} + +static void +impl_modem_cdma_get_signal_quality (MMModemCdma *modem, DBusGMethodInvocation *context) +{ + mm_modem_cdma_get_signal_quality (modem, uint_call_done, context); +} + +void +mm_modem_cdma_emit_signal_quality_changed (MMModemCdma *self, guint32 quality) +{ + g_return_if_fail (MM_IS_MODEM_CDMA (self)); + + g_signal_emit (self, signals[SIGNAL_QUALITY], 0, quality); +} + +/*****************************************************************************/ + +static void +get_registration_state_call_done (MMModemCdma *self, + MMModemCdmaRegistrationState cdma_1x_reg_state, + MMModemCdmaRegistrationState evdo_reg_state, + GError *error, + gpointer user_data) +{ + DBusGMethodInvocation *context = (DBusGMethodInvocation *) user_data; + + if (error) + dbus_g_method_return_error (context, error); + else + dbus_g_method_return (context, cdma_1x_reg_state, evdo_reg_state); +} + +void +mm_modem_cdma_get_registration_state (MMModemCdma *self, + MMModemCdmaRegistrationStateFn callback, + gpointer user_data) +{ + g_return_if_fail (MM_IS_MODEM_CDMA (self)); + g_return_if_fail (callback != NULL); + + if (MM_MODEM_CDMA_GET_INTERFACE (self)->get_registration_state) + MM_MODEM_CDMA_GET_INTERFACE (self)->get_registration_state (self, callback, user_data); + else { + GError *error; + + error = g_error_new_literal (MM_MODEM_ERROR, + MM_MODEM_ERROR_OPERATION_NOT_SUPPORTED, + "Operation not supported"); + + callback (self, + MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN, + MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN, + error, + user_data); + + g_error_free (error); + } +} + +static void +impl_modem_cdma_get_registration_state (MMModemCdma *modem, DBusGMethodInvocation *context) +{ + mm_modem_cdma_get_registration_state (modem, get_registration_state_call_done, context); +} + +void +mm_modem_cdma_emit_registration_state_changed (MMModemCdma *self, + MMModemCdmaRegistrationState cdma_1x_new_state, + MMModemCdmaRegistrationState evdo_new_state) +{ + g_return_if_fail (MM_IS_MODEM_CDMA (self)); + + g_signal_emit (self, signals[REGISTRATION_STATE_CHANGED], 0, cdma_1x_new_state, evdo_new_state); +} + +/*****************************************************************************/ + +static void +mm_modem_cdma_init (gpointer g_iface) +{ + GType iface_type = G_TYPE_FROM_INTERFACE (g_iface); + static gboolean initialized = FALSE; + + if (initialized) + return; + + /* Signals */ + signals[SIGNAL_QUALITY] = + g_signal_new ("signal-quality", + iface_type, + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (MMModemCdma, signal_quality), + NULL, NULL, + g_cclosure_marshal_VOID__UINT, + G_TYPE_NONE, 1, G_TYPE_UINT); + + signals[REGISTRATION_STATE_CHANGED] = + g_signal_new ("registration-state-changed", + iface_type, + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (MMModemCdma, registration_state_changed), + NULL, NULL, + mm_marshal_VOID__UINT_UINT, + G_TYPE_NONE, 2, G_TYPE_UINT, G_TYPE_UINT); + + initialized = TRUE; +} + +GType +mm_modem_cdma_get_type (void) +{ + static GType modem_type = 0; + + if (!G_UNLIKELY (modem_type)) { + const GTypeInfo modem_info = { + sizeof (MMModemCdma), /* class_size */ + mm_modem_cdma_init, /* base_init */ + NULL, /* base_finalize */ + NULL, + NULL, /* class_finalize */ + NULL, /* class_data */ + 0, + 0, /* n_preallocs */ + NULL + }; + + modem_type = g_type_register_static (G_TYPE_INTERFACE, + "MMModemCdma", + &modem_info, 0); + + g_type_interface_add_prerequisite (modem_type, MM_TYPE_MODEM); + + dbus_g_object_type_install_info (modem_type, &dbus_glib_mm_modem_cdma_object_info); + } + + return modem_type; +} diff --git a/src/mm-modem-cdma.h b/src/mm-modem-cdma.h new file mode 100644 index 0000000..a7a626f --- /dev/null +++ b/src/mm-modem-cdma.h @@ -0,0 +1,108 @@ +/* -*- 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 Novell, Inc. + * Copyright (C) 2009 Red Hat, Inc. + */ + +#ifndef MM_MODEM_CDMA_H +#define MM_MODEM_CDMA_H + +#include <mm-modem.h> + +typedef enum { + MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN = 0, + MM_MODEM_CDMA_REGISTRATION_STATE_REGISTERED = 1, + MM_MODEM_CDMA_REGISTRATION_STATE_HOME = 2, + MM_MODEM_CDMA_REGISTRATION_STATE_ROAMING = 3, + + MM_MODEM_CDMA_REGISTRATION_STATE_LAST = MM_MODEM_CDMA_REGISTRATION_STATE_ROAMING +} MMModemCdmaRegistrationState; + +#define MM_TYPE_MODEM_CDMA (mm_modem_cdma_get_type ()) +#define MM_MODEM_CDMA(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_MODEM_CDMA, MMModemCdma)) +#define MM_IS_MODEM_CDMA(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_MODEM_CDMA)) +#define MM_MODEM_CDMA_GET_INTERFACE(obj) (G_TYPE_INSTANCE_GET_INTERFACE ((obj), MM_TYPE_MODEM_CDMA, MMModemCdma)) + +#define MM_MODEM_CDMA_REGISTRATION_STATE_CHANGED "registration-state-changed" + +typedef struct _MMModemCdma MMModemCdma; + +typedef void (*MMModemCdmaServingSystemFn) (MMModemCdma *modem, + guint32 class, + unsigned char band, + guint32 sid, + GError *error, + gpointer user_data); + +typedef void (*MMModemCdmaRegistrationStateFn) (MMModemCdma *modem, + MMModemCdmaRegistrationState cdma_1x_reg_state, + MMModemCdmaRegistrationState evdo_reg_state, + GError *error, + gpointer user_data); + +struct _MMModemCdma { + GTypeInterface g_iface; + + /* Methods */ + void (*get_signal_quality) (MMModemCdma *self, + MMModemUIntFn callback, + gpointer user_data); + + void (*get_esn) (MMModemCdma *self, + MMModemStringFn callback, + gpointer user_data); + + void (*get_serving_system) (MMModemCdma *self, + MMModemCdmaServingSystemFn callback, + gpointer user_data); + + void (*get_registration_state) (MMModemCdma *self, + MMModemCdmaRegistrationStateFn callback, + gpointer user_data); + + /* Signals */ + void (*signal_quality) (MMModemCdma *self, + guint32 quality); + + void (*registration_state_changed) (MMModemCdma *self, + MMModemCdmaRegistrationState cdma_1x_new_state, + MMModemCdmaRegistrationState evdo_new_state); +}; + +GType mm_modem_cdma_get_type (void); + +void mm_modem_cdma_get_signal_quality (MMModemCdma *self, + MMModemUIntFn callback, + gpointer user_data); + +void mm_modem_cdma_get_esn (MMModemCdma *self, + MMModemStringFn callback, + gpointer user_data); + +void mm_modem_cdma_get_serving_system (MMModemCdma *self, + MMModemCdmaServingSystemFn callback, + gpointer user_data); + +void mm_modem_cdma_get_registration_state (MMModemCdma *self, + MMModemCdmaRegistrationStateFn callback, + gpointer user_data); + +/* Protected */ + +void mm_modem_cdma_emit_signal_quality_changed (MMModemCdma *self, guint32 new_quality); + +void mm_modem_cdma_emit_registration_state_changed (MMModemCdma *self, + MMModemCdmaRegistrationState cdma_1x_new_state, + MMModemCdmaRegistrationState evdo_new_state); + +#endif /* MM_MODEM_CDMA_H */ diff --git a/src/mm-modem-gsm-card.c b/src/mm-modem-gsm-card.c new file mode 100644 index 0000000..dea4590 --- /dev/null +++ b/src/mm-modem-gsm-card.c @@ -0,0 +1,312 @@ +/* -*- 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 Novell, Inc. + * Copyright (C) 2009 Red Hat, Inc. + */ + +#include <dbus/dbus-glib.h> + +#include "mm-modem-gsm-card.h" +#include "mm-errors.h" +#include "mm-callback-info.h" +#include "mm-modem-gsm.h" + +static void impl_gsm_modem_get_imei (MMModemGsmCard *modem, + DBusGMethodInvocation *context); + +static void impl_gsm_modem_get_imsi (MMModemGsmCard *modem, + DBusGMethodInvocation *context); + +static void impl_gsm_modem_send_pin (MMModemGsmCard *modem, + const char *pin, + DBusGMethodInvocation *context); + +static void impl_gsm_modem_send_puk (MMModemGsmCard *modem, + const char *puk, + const char *pin, + DBusGMethodInvocation *context); + +static void impl_gsm_modem_enable_pin (MMModemGsmCard *modem, + const char *pin, + gboolean enabled, + DBusGMethodInvocation *context); + +static void impl_gsm_modem_change_pin (MMModemGsmCard *modem, + const char *old_pin, + const char *new_pin, + DBusGMethodInvocation *context); + +#include "mm-modem-gsm-card-glue.h" + +/*****************************************************************************/ + +static void +str_call_done (MMModem *modem, const char *result, GError *error, gpointer user_data) +{ + DBusGMethodInvocation *context = (DBusGMethodInvocation *) user_data; + + if (error) + dbus_g_method_return_error (context, error); + else + dbus_g_method_return (context, result); +} + +static void +str_call_not_supported (MMModemGsmCard *self, + MMModemStringFn callback, + gpointer user_data) +{ + MMCallbackInfo *info; + + info = mm_callback_info_string_new (MM_MODEM (self), callback, user_data); + info->error = g_error_new_literal (MM_MODEM_ERROR, MM_MODEM_ERROR_OPERATION_NOT_SUPPORTED, + "Operation not supported"); + + mm_callback_info_schedule (info); +} + +static void +async_call_done (MMModem *modem, GError *error, gpointer user_data) +{ + DBusGMethodInvocation *context = (DBusGMethodInvocation *) user_data; + + if (error) + dbus_g_method_return_error (context, error); + else + dbus_g_method_return (context); +} + +static void +async_call_not_supported (MMModemGsmCard *self, + MMModemFn callback, + gpointer user_data) +{ + MMCallbackInfo *info; + + info = mm_callback_info_new (MM_MODEM (self), callback, user_data); + info->error = g_error_new_literal (MM_MODEM_ERROR, MM_MODEM_ERROR_OPERATION_NOT_SUPPORTED, + "Operation not supported"); + mm_callback_info_schedule (info); +} + +/*****************************************************************************/ + +void +mm_modem_gsm_card_get_imei (MMModemGsmCard *self, + MMModemStringFn callback, + gpointer user_data) +{ + g_return_if_fail (MM_IS_MODEM_GSM_CARD (self)); + g_return_if_fail (callback != NULL); + + if (MM_MODEM_GSM_CARD_GET_INTERFACE (self)->get_imei) + MM_MODEM_GSM_CARD_GET_INTERFACE (self)->get_imei (self, callback, user_data); + else + str_call_not_supported (self, callback, user_data); +} + +void +mm_modem_gsm_card_get_imsi (MMModemGsmCard *self, + MMModemStringFn callback, + gpointer user_data) +{ + g_return_if_fail (MM_IS_MODEM_GSM_CARD (self)); + g_return_if_fail (callback != NULL); + + if (MM_MODEM_GSM_CARD_GET_INTERFACE (self)->get_imsi) + MM_MODEM_GSM_CARD_GET_INTERFACE (self)->get_imsi (self, callback, user_data); + else + str_call_not_supported (self, callback, user_data); +} + +void +mm_modem_gsm_card_send_puk (MMModemGsmCard *self, + const char *puk, + const char *pin, + MMModemFn callback, + gpointer user_data) +{ + g_return_if_fail (MM_IS_MODEM_GSM_CARD (self)); + g_return_if_fail (puk != NULL); + g_return_if_fail (pin != NULL); + g_return_if_fail (callback != NULL); + + if (MM_MODEM_GSM_CARD_GET_INTERFACE (self)->send_puk) + MM_MODEM_GSM_CARD_GET_INTERFACE (self)->send_puk (self, puk, pin, callback, user_data); + else + async_call_not_supported (self, callback, user_data); +} + +void +mm_modem_gsm_card_send_pin (MMModemGsmCard *self, + const char *pin, + MMModemFn callback, + gpointer user_data) +{ + g_return_if_fail (MM_IS_MODEM_GSM_CARD (self)); + g_return_if_fail (pin != NULL); + g_return_if_fail (callback != NULL); + + if (MM_MODEM_GSM_CARD_GET_INTERFACE (self)->send_pin) + MM_MODEM_GSM_CARD_GET_INTERFACE (self)->send_pin (self, pin, callback, user_data); + else + async_call_not_supported (self, callback, user_data); +} + +void +mm_modem_gsm_card_enable_pin (MMModemGsmCard *self, + const char *pin, + gboolean enabled, + MMModemFn callback, + gpointer user_data) +{ + g_return_if_fail (MM_IS_MODEM_GSM_CARD (self)); + g_return_if_fail (pin != NULL); + g_return_if_fail (callback != NULL); + + if (MM_MODEM_GSM_CARD_GET_INTERFACE (self)->enable_pin) + MM_MODEM_GSM_CARD_GET_INTERFACE (self)->enable_pin (self, pin, enabled, callback, user_data); + else + async_call_not_supported (self, callback, user_data); +} + +void +mm_modem_gsm_card_change_pin (MMModemGsmCard *self, + const char *old_pin, + const char *new_pin, + MMModemFn callback, + gpointer user_data) +{ + g_return_if_fail (MM_IS_MODEM_GSM_CARD (self)); + g_return_if_fail (old_pin != NULL); + g_return_if_fail (new_pin != NULL); + g_return_if_fail (callback != NULL); + + if (MM_MODEM_GSM_CARD_GET_INTERFACE (self)->change_pin) + MM_MODEM_GSM_CARD_GET_INTERFACE (self)->change_pin (self, old_pin, new_pin, callback, user_data); + else + async_call_not_supported (self, callback, user_data); +} + +/*****************************************************************************/ + +static void +impl_gsm_modem_get_imei (MMModemGsmCard *modem, + DBusGMethodInvocation *context) +{ + mm_modem_gsm_card_get_imei (modem, str_call_done, context); +} + +static void +impl_gsm_modem_get_imsi (MMModemGsmCard *modem, + DBusGMethodInvocation *context) +{ + mm_modem_gsm_card_get_imsi (modem, str_call_done, context); +} + +static void + impl_gsm_modem_send_puk (MMModemGsmCard *modem, + const char *puk, + const char *pin, + DBusGMethodInvocation *context) +{ + mm_modem_gsm_card_send_puk (modem, puk, pin, async_call_done, context); +} + +static void +impl_gsm_modem_send_pin (MMModemGsmCard *modem, + const char *pin, + DBusGMethodInvocation *context) +{ + mm_modem_gsm_card_send_pin (modem, pin, async_call_done, context); +} + +static void +impl_gsm_modem_enable_pin (MMModemGsmCard *modem, + const char *pin, + gboolean enabled, + DBusGMethodInvocation *context) +{ + mm_modem_gsm_card_enable_pin (modem, pin, enabled, async_call_done, context); +} + +static void +impl_gsm_modem_change_pin (MMModemGsmCard *modem, + const char *old_pin, + const char *new_pin, + DBusGMethodInvocation *context) +{ + mm_modem_gsm_card_change_pin (modem, old_pin, new_pin, async_call_done, context); +} + +/*****************************************************************************/ + +static void +mm_modem_gsm_card_init (gpointer g_iface) +{ + static gboolean initialized = FALSE; + + if (G_LIKELY (initialized)) + return; + + initialized = TRUE; + + g_object_interface_install_property + (g_iface, + g_param_spec_uint (MM_MODEM_GSM_CARD_SUPPORTED_BANDS, + "Supported Modes", + "Supported frequency bands of the card", + MM_MODEM_GSM_BAND_UNKNOWN, + MM_MODEM_GSM_BAND_LAST, + MM_MODEM_GSM_BAND_UNKNOWN, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); + + g_object_interface_install_property + (g_iface, + g_param_spec_uint (MM_MODEM_GSM_CARD_SUPPORTED_MODES, + "Supported Modes", + "Supported modes of the card (ex 2G preferred, 3G preferred, 2G only, etc", + MM_MODEM_GSM_MODE_UNKNOWN, + MM_MODEM_GSM_MODE_LAST, + MM_MODEM_GSM_MODE_UNKNOWN, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); +} + +GType +mm_modem_gsm_card_get_type (void) +{ + static GType card_type = 0; + + if (G_UNLIKELY (!card_type)) { + const GTypeInfo card_info = { + sizeof (MMModemGsmCard), /* class_size */ + mm_modem_gsm_card_init, /* base_init */ + NULL, /* base_finalize */ + NULL, + NULL, /* class_finalize */ + NULL, /* class_data */ + 0, + 0, /* n_preallocs */ + NULL + }; + + card_type = g_type_register_static (G_TYPE_INTERFACE, + "MMModemGsmCard", + &card_info, 0); + + g_type_interface_add_prerequisite (card_type, G_TYPE_OBJECT); + dbus_g_object_type_install_info (card_type, &dbus_glib_mm_modem_gsm_card_object_info); + } + + return card_type; +} diff --git a/src/mm-modem-gsm-card.h b/src/mm-modem-gsm-card.h new file mode 100644 index 0000000..4d690e6 --- /dev/null +++ b/src/mm-modem-gsm-card.h @@ -0,0 +1,101 @@ +/* -*- 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 Novell, Inc. + * Copyright (C) 2009 Red Hat, Inc. + */ + +#ifndef MM_MODEM_GSM_CARD_H +#define MM_MODEM_GSM_CARD_H + +#include <mm-modem.h> + +#define MM_TYPE_MODEM_GSM_CARD (mm_modem_gsm_card_get_type ()) +#define MM_MODEM_GSM_CARD(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_MODEM_GSM_CARD, MMModemGsmCard)) +#define MM_IS_MODEM_GSM_CARD(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_MODEM_GSM_CARD)) +#define MM_MODEM_GSM_CARD_GET_INTERFACE(obj) (G_TYPE_INSTANCE_GET_INTERFACE ((obj), MM_TYPE_MODEM_GSM_CARD, MMModemGsmCard)) + +#define MM_MODEM_GSM_CARD_SUPPORTED_BANDS "supported-bands" +#define MM_MODEM_GSM_CARD_SUPPORTED_MODES "supported-modes" + +typedef struct _MMModemGsmCard MMModemGsmCard; + +struct _MMModemGsmCard { + GTypeInterface g_iface; + + /* Methods */ + void (*get_imei) (MMModemGsmCard *self, + MMModemStringFn callback, + gpointer user_data); + + void (*get_imsi) (MMModemGsmCard *self, + MMModemStringFn callback, + gpointer user_data); + + void (*send_puk) (MMModemGsmCard *self, + const char *puk, + const char *pin, + MMModemFn callback, + gpointer user_data); + + void (*send_pin) (MMModemGsmCard *self, + const char *pin, + MMModemFn callback, + gpointer user_data); + + void (*enable_pin) (MMModemGsmCard *self, + const char *pin, + gboolean enabled, + MMModemFn callback, + gpointer user_data); + + void (*change_pin) (MMModemGsmCard *self, + const char *old_pin, + const char *new_pin, + MMModemFn callback, + gpointer user_data); +}; + +GType mm_modem_gsm_card_get_type (void); + +void mm_modem_gsm_card_get_imei (MMModemGsmCard *self, + MMModemStringFn callback, + gpointer user_data); + +void mm_modem_gsm_card_get_imsi (MMModemGsmCard *self, + MMModemStringFn callback, + gpointer user_data); + +void mm_modem_gsm_card_send_puk (MMModemGsmCard *self, + const char *puk, + const char *pin, + MMModemFn callback, + gpointer user_data); + +void mm_modem_gsm_card_send_pin (MMModemGsmCard *self, + const char *pin, + MMModemFn callback, + gpointer user_data); + +void mm_modem_gsm_card_enable_pin (MMModemGsmCard *self, + const char *pin, + gboolean enabled, + MMModemFn callback, + gpointer user_data); + +void mm_modem_gsm_card_change_pin (MMModemGsmCard *self, + const char *old_pin, + const char *new_pin, + MMModemFn callback, + gpointer user_data); + +#endif /* MM_MODEM_GSM_CARD_H */ diff --git a/src/mm-modem-gsm-network.c b/src/mm-modem-gsm-network.c new file mode 100644 index 0000000..26ff422 --- /dev/null +++ b/src/mm-modem-gsm-network.c @@ -0,0 +1,569 @@ +/* -*- 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 Novell, Inc. + */ + +#include <string.h> +#include <dbus/dbus-glib.h> + +#include "mm-modem-gsm-network.h" +#include "mm-errors.h" +#include "mm-callback-info.h" +#include "mm-marshal.h" + +static void impl_gsm_modem_register (MMModemGsmNetwork *modem, + const char *network_id, + DBusGMethodInvocation *context); + +static void impl_gsm_modem_scan (MMModemGsmNetwork *modem, + DBusGMethodInvocation *context); + +static void impl_gsm_modem_set_apn (MMModemGsmNetwork *modem, + const char *apn, + DBusGMethodInvocation *context); + +static void impl_gsm_modem_get_signal_quality (MMModemGsmNetwork *modem, + DBusGMethodInvocation *context); + +static void impl_gsm_modem_set_band (MMModemGsmNetwork *modem, + MMModemGsmBand band, + DBusGMethodInvocation *context); + +static void impl_gsm_modem_get_band (MMModemGsmNetwork *modem, + DBusGMethodInvocation *context); + +static void impl_gsm_modem_set_network_mode (MMModemGsmNetwork *modem, + MMModemGsmMode mode, + DBusGMethodInvocation *context); + +static void impl_gsm_modem_get_network_mode (MMModemGsmNetwork *modem, + DBusGMethodInvocation *context); + +static void impl_gsm_modem_get_reg_info (MMModemGsmNetwork *modem, + DBusGMethodInvocation *context); + +#include "mm-modem-gsm-network-glue.h" + +/*****************************************************************************/ + +enum { + SIGNAL_QUALITY, + REGISTRATION_INFO, + NETWORK_MODE, + + LAST_SIGNAL +}; + +static guint signals[LAST_SIGNAL] = { 0 }; + +/*****************************************************************************/ + +static void +async_call_done (MMModem *modem, GError *error, gpointer user_data) +{ + DBusGMethodInvocation *context = (DBusGMethodInvocation *) user_data; + + if (error) + dbus_g_method_return_error (context, error); + else + dbus_g_method_return (context); +} + +static void +async_call_not_supported (MMModemGsmNetwork *self, + MMModemFn callback, + gpointer user_data) +{ + MMCallbackInfo *info; + + info = mm_callback_info_new (MM_MODEM (self), callback, user_data); + info->error = g_error_new_literal (MM_MODEM_ERROR, MM_MODEM_ERROR_OPERATION_NOT_SUPPORTED, + "Operation not supported"); + mm_callback_info_schedule (info); +} + +static void +uint_call_done (MMModem *modem, guint32 result, GError *error, gpointer user_data) +{ + DBusGMethodInvocation *context = (DBusGMethodInvocation *) user_data; + + if (error) + dbus_g_method_return_error (context, error); + else + dbus_g_method_return (context, result); +} + +static void +uint_call_not_supported (MMModemGsmNetwork *self, + MMModemUIntFn callback, + gpointer user_data) +{ + MMCallbackInfo *info; + + info = mm_callback_info_uint_new (MM_MODEM (self), callback, user_data); + info->error = g_error_new_literal (MM_MODEM_ERROR, MM_MODEM_ERROR_OPERATION_NOT_SUPPORTED, + "Operation not supported"); + mm_callback_info_schedule (info); +} + +static void +reg_info_call_done (MMModemGsmNetwork *self, + MMModemGsmNetworkRegStatus status, + const char *oper_code, + const char *oper_name, + GError *error, + gpointer user_data) +{ + DBusGMethodInvocation *context = (DBusGMethodInvocation *) user_data; + + if (error) + dbus_g_method_return_error (context, error); + else { + GValueArray *array; + GValue value = { 0, }; + + array = g_value_array_new (3); + + /* Status */ + g_value_init (&value, G_TYPE_UINT); + g_value_set_uint (&value, (guint32) status); + g_value_array_append (array, &value); + g_value_unset (&value); + + /* Operator code */ + g_value_init (&value, G_TYPE_STRING); + g_value_set_string (&value, oper_code); + g_value_array_append (array, &value); + g_value_unset (&value); + + /* Operator name */ + g_value_init (&value, G_TYPE_STRING); + g_value_set_string (&value, oper_name); + g_value_array_append (array, &value); + g_value_unset (&value); + + dbus_g_method_return (context, array); + } +} + +static void +reg_info_invoke (MMCallbackInfo *info) +{ + MMModemGsmNetworkRegInfoFn callback = (MMModemGsmNetworkRegInfoFn) info->callback; + + callback (MM_MODEM_GSM_NETWORK (info->modem), 0, NULL, NULL, info->error, info->user_data); +} + +static void +reg_info_call_not_supported (MMModemGsmNetwork *self, + MMModemGsmNetworkRegInfoFn callback, + gpointer user_data) +{ + MMCallbackInfo *info; + + info = mm_callback_info_new_full (MM_MODEM (self), reg_info_invoke, G_CALLBACK (callback), user_data); + info->error = g_error_new_literal (MM_MODEM_ERROR, MM_MODEM_ERROR_OPERATION_NOT_SUPPORTED, + "Operation not supported"); + + mm_callback_info_schedule (info); +} + +static void +scan_call_done (MMModemGsmNetwork *self, + GPtrArray *results, + GError *error, + gpointer user_data) +{ + DBusGMethodInvocation *context = (DBusGMethodInvocation *) user_data; + + if (error) + dbus_g_method_return_error (context, error); + else + dbus_g_method_return (context, results); +} + +static void +gsm_network_scan_invoke (MMCallbackInfo *info) +{ + MMModemGsmNetworkScanFn callback = (MMModemGsmNetworkScanFn) info->callback; + + callback (MM_MODEM_GSM_NETWORK (info->modem), NULL, info->error, info->user_data); +} + +static void +scan_call_not_supported (MMModemGsmNetwork *self, + MMModemGsmNetworkScanFn callback, + gpointer user_data) +{ + MMCallbackInfo *info; + + info = mm_callback_info_new_full (MM_MODEM (self), gsm_network_scan_invoke, G_CALLBACK (callback), user_data); + info->error = g_error_new_literal (MM_MODEM_ERROR, MM_MODEM_ERROR_OPERATION_NOT_SUPPORTED, + "Operation not supported"); + + mm_callback_info_schedule (info); +} + +/*****************************************************************************/ + +void +mm_modem_gsm_network_register (MMModemGsmNetwork *self, + const char *network_id, + MMModemFn callback, + gpointer user_data) +{ + g_return_if_fail (MM_IS_MODEM_GSM_NETWORK (self)); + g_return_if_fail (callback != NULL); + + if (MM_MODEM_GSM_NETWORK_GET_INTERFACE (self)->do_register) + MM_MODEM_GSM_NETWORK_GET_INTERFACE (self)->do_register (self, network_id, callback, user_data); + else + async_call_not_supported (self, callback, user_data); +} + +void +mm_modem_gsm_network_scan (MMModemGsmNetwork *self, + MMModemGsmNetworkScanFn callback, + gpointer user_data) +{ + g_return_if_fail (MM_IS_MODEM_GSM_NETWORK (self)); + g_return_if_fail (callback != NULL); + + if (MM_MODEM_GSM_NETWORK_GET_INTERFACE (self)->scan) + MM_MODEM_GSM_NETWORK_GET_INTERFACE (self)->scan (self, callback, user_data); + else + scan_call_not_supported (self, callback, user_data); +} + +void +mm_modem_gsm_network_set_apn (MMModemGsmNetwork *self, + const char *apn, + MMModemFn callback, + gpointer user_data) +{ + g_return_if_fail (MM_IS_MODEM_GSM_NETWORK (self)); + g_return_if_fail (apn != NULL); + g_return_if_fail (callback != NULL); + + if (MM_MODEM_GSM_NETWORK_GET_INTERFACE (self)->set_apn) + MM_MODEM_GSM_NETWORK_GET_INTERFACE (self)->set_apn (self, apn, callback, user_data); + else + async_call_not_supported (self, callback, user_data); +} + +void +mm_modem_gsm_network_get_signal_quality (MMModemGsmNetwork *self, + MMModemUIntFn callback, + gpointer user_data) +{ + g_return_if_fail (MM_IS_MODEM_GSM_NETWORK (self)); + g_return_if_fail (callback != NULL); + + if (MM_MODEM_GSM_NETWORK_GET_INTERFACE (self)->get_signal_quality) + MM_MODEM_GSM_NETWORK_GET_INTERFACE (self)->get_signal_quality (self, callback, user_data); + else + uint_call_not_supported (self, callback, user_data); +} + +void +mm_modem_gsm_network_set_band (MMModemGsmNetwork *self, + MMModemGsmBand band, + MMModemFn callback, + gpointer user_data) +{ + g_return_if_fail (MM_IS_MODEM_GSM_NETWORK (self)); + g_return_if_fail (callback != NULL); + + if (MM_MODEM_GSM_NETWORK_GET_INTERFACE (self)->set_band) + MM_MODEM_GSM_NETWORK_GET_INTERFACE (self)->set_band (self, band, callback, user_data); + else + async_call_not_supported (self, callback, user_data); +} + +void +mm_modem_gsm_network_get_band (MMModemGsmNetwork *self, + MMModemUIntFn callback, + gpointer user_data) +{ + g_return_if_fail (MM_IS_MODEM_GSM_NETWORK (self)); + g_return_if_fail (callback != NULL); + + if (MM_MODEM_GSM_NETWORK_GET_INTERFACE (self)->get_band) + MM_MODEM_GSM_NETWORK_GET_INTERFACE (self)->get_band (self, callback, user_data); + else + uint_call_not_supported (self, callback, user_data); +} + +void +mm_modem_gsm_network_set_mode (MMModemGsmNetwork *self, + MMModemGsmMode mode, + MMModemFn callback, + gpointer user_data) +{ + g_return_if_fail (MM_IS_MODEM_GSM_NETWORK (self)); + g_return_if_fail (callback != NULL); + + if (MM_MODEM_GSM_NETWORK_GET_INTERFACE (self)->set_network_mode) + MM_MODEM_GSM_NETWORK_GET_INTERFACE (self)->set_network_mode (self, mode, callback, user_data); + else + async_call_not_supported (self, callback, user_data); +} + +void +mm_modem_gsm_network_get_mode (MMModemGsmNetwork *self, + MMModemUIntFn callback, + gpointer user_data) +{ + g_return_if_fail (MM_IS_MODEM_GSM_NETWORK (self)); + g_return_if_fail (callback != NULL); + + if (MM_MODEM_GSM_NETWORK_GET_INTERFACE (self)->get_network_mode) + MM_MODEM_GSM_NETWORK_GET_INTERFACE (self)->get_network_mode (self, callback, user_data); + else + uint_call_not_supported (self, callback, user_data); +} + +void +mm_modem_gsm_network_get_registration_info (MMModemGsmNetwork *self, + MMModemGsmNetworkRegInfoFn callback, + gpointer user_data) +{ + g_return_if_fail (MM_IS_MODEM_GSM_NETWORK (self)); + g_return_if_fail (callback != NULL); + + if (MM_MODEM_GSM_NETWORK_GET_INTERFACE (self)->get_registration_info) + MM_MODEM_GSM_NETWORK_GET_INTERFACE (self)->get_registration_info (self, callback, user_data); + else + reg_info_call_not_supported (self, callback, user_data); +} + +void +mm_modem_gsm_network_signal_quality (MMModemGsmNetwork *self, + guint32 quality) +{ + g_return_if_fail (MM_IS_MODEM_GSM_NETWORK (self)); + + g_signal_emit (self, signals[SIGNAL_QUALITY], 0, quality); +} + +void +mm_modem_gsm_network_registration_info (MMModemGsmNetwork *self, + MMModemGsmNetworkRegStatus status, + const char *oper_code, + const char *oper_name) +{ + g_return_if_fail (MM_IS_MODEM_GSM_NETWORK (self)); + + g_signal_emit (self, signals[REGISTRATION_INFO], 0, status, + oper_code ? oper_code : "", + oper_name ? oper_name : ""); +} + +void +mm_modem_gsm_network_mode (MMModemGsmNetwork *self, + MMModemGsmMode mode) +{ + g_return_if_fail (MM_IS_MODEM_GSM_NETWORK (self)); + + g_signal_emit (self, signals[NETWORK_MODE], 0, mode); +} + +/*****************************************************************************/ + +static void +impl_gsm_modem_register (MMModemGsmNetwork *modem, + const char *network_id, + DBusGMethodInvocation *context) +{ + const char *id; + + /* DBus does not support NULL strings, so the caller should pass an empty string + for manual registration. */ + if (strlen (network_id) < 1) + id = NULL; + else + id = network_id; + + mm_modem_gsm_network_register (modem, id, async_call_done, context); +} + +static void +impl_gsm_modem_scan (MMModemGsmNetwork *modem, + DBusGMethodInvocation *context) +{ + mm_modem_gsm_network_scan (modem, scan_call_done, context); +} + +static void +impl_gsm_modem_set_apn (MMModemGsmNetwork *modem, + const char *apn, + DBusGMethodInvocation *context) +{ + mm_modem_gsm_network_set_apn (modem, apn, async_call_done, context); +} + +static void +impl_gsm_modem_get_signal_quality (MMModemGsmNetwork *modem, + DBusGMethodInvocation *context) +{ + mm_modem_gsm_network_get_signal_quality (modem, uint_call_done, context); +} + +static gboolean +check_for_single_value (guint32 value) +{ + gboolean found = FALSE; + guint32 i; + + for (i = 1; i <= 32; i++) { + if (value & 0x1) { + if (found) + return FALSE; /* More than one bit set */ + found = TRUE; + } + value >>= 1; + } + + return TRUE; +} + +static void +impl_gsm_modem_set_band (MMModemGsmNetwork *modem, + MMModemGsmBand band, + DBusGMethodInvocation *context) +{ + if (!check_for_single_value (band)) { + GError *error; + + error = g_error_new_literal (MM_MODEM_ERROR, MM_MODEM_ERROR_OPERATION_NOT_SUPPORTED, + "Invalid arguments (more than one value given)"); + dbus_g_method_return_error (context, error); + g_error_free (error); + return; + } + + mm_modem_gsm_network_set_band (modem, band, async_call_done, context); +} + +static void +impl_gsm_modem_get_band (MMModemGsmNetwork *modem, + DBusGMethodInvocation *context) +{ + mm_modem_gsm_network_get_band (modem, uint_call_done, context); +} + +static void +impl_gsm_modem_set_network_mode (MMModemGsmNetwork *modem, + MMModemGsmMode mode, + DBusGMethodInvocation *context) +{ + if (!check_for_single_value (mode)) { + GError *error; + + error = g_error_new_literal (MM_MODEM_ERROR, MM_MODEM_ERROR_OPERATION_NOT_SUPPORTED, + "Invalid arguments (more than one value given)"); + dbus_g_method_return_error (context, error); + g_error_free (error); + return; + } + + mm_modem_gsm_network_set_mode (modem, mode, async_call_done, context); +} + +static void +impl_gsm_modem_get_network_mode (MMModemGsmNetwork *modem, + DBusGMethodInvocation *context) +{ + mm_modem_gsm_network_get_mode (modem, uint_call_done, context); +} + +static void +impl_gsm_modem_get_reg_info (MMModemGsmNetwork *modem, + DBusGMethodInvocation *context) +{ + mm_modem_gsm_network_get_registration_info (modem, reg_info_call_done, context); +} + +/*****************************************************************************/ + +static void +mm_modem_gsm_network_init (gpointer g_iface) +{ + GType iface_type = G_TYPE_FROM_INTERFACE (g_iface); + static gboolean initialized = FALSE; + + if (initialized) + return; + + /* Signals */ + signals[SIGNAL_QUALITY] = + g_signal_new ("signal-quality", + iface_type, + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (MMModemGsmNetwork, signal_quality), + NULL, NULL, + g_cclosure_marshal_VOID__UINT, + G_TYPE_NONE, 1, + G_TYPE_UINT); + + signals[REGISTRATION_INFO] = + g_signal_new ("registration-info", + iface_type, + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (MMModemGsmNetwork, registration_info), + NULL, NULL, + mm_marshal_VOID__UINT_STRING_STRING, + G_TYPE_NONE, 3, + G_TYPE_UINT, G_TYPE_STRING, G_TYPE_STRING); + + signals[NETWORK_MODE] = + g_signal_new ("network-mode", + iface_type, + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (MMModemGsmNetwork, network_mode), + NULL, NULL, + g_cclosure_marshal_VOID__UINT, + G_TYPE_NONE, 1, + G_TYPE_UINT); + + initialized = TRUE; +} + +GType +mm_modem_gsm_network_get_type (void) +{ + static GType network_type = 0; + + if (!G_UNLIKELY (network_type)) { + const GTypeInfo network_info = { + sizeof (MMModemGsmNetwork), /* class_size */ + mm_modem_gsm_network_init, /* base_init */ + NULL, /* base_finalize */ + NULL, + NULL, /* class_finalize */ + NULL, /* class_data */ + 0, + 0, /* n_preallocs */ + NULL + }; + + network_type = g_type_register_static (G_TYPE_INTERFACE, + "MMModemGsmNetwork", + &network_info, 0); + + g_type_interface_add_prerequisite (network_type, G_TYPE_OBJECT); + dbus_g_object_type_install_info (network_type, &dbus_glib_mm_modem_gsm_network_object_info); + } + + return network_type; +} diff --git a/src/mm-modem-gsm-network.h b/src/mm-modem-gsm-network.h new file mode 100644 index 0000000..493baec --- /dev/null +++ b/src/mm-modem-gsm-network.h @@ -0,0 +1,164 @@ +/* -*- 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 Novell, Inc. + * Copyright (C) 2009 Red Hat, Inc. + */ + +#ifndef MM_MODEM_GSM_NETWORK_H +#define MM_MODEM_GSM_NETWORK_H + +#include <mm-modem.h> +#include <mm-modem-gsm.h> + +#define MM_TYPE_MODEM_GSM_NETWORK (mm_modem_gsm_network_get_type ()) +#define MM_MODEM_GSM_NETWORK(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_MODEM_GSM_NETWORK, MMModemGsmNetwork)) +#define MM_IS_MODEM_GSM_NETWORK(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_MODEM_GSM_NETWORK)) +#define MM_MODEM_GSM_NETWORK_GET_INTERFACE(obj) (G_TYPE_INSTANCE_GET_INTERFACE ((obj), MM_TYPE_MODEM_GSM_NETWORK, MMModemGsmNetwork)) + +typedef enum { + MM_MODEM_GSM_NETWORK_REG_STATUS_IDLE = 0, + MM_MODEM_GSM_NETWORK_REG_STATUS_HOME = 1, + MM_MODEM_GSM_NETWORK_REG_STATUS_SEARCHING = 2, + MM_MODEM_GSM_NETWORK_REG_STATUS_DENIED = 3, + MM_MODEM_GSM_NETWORK_REG_STATUS_UNKNOWN = 4, + MM_MODEM_GSM_NETWORK_REG_STATUS_ROAMING = 5 +} MMModemGsmNetworkRegStatus; + +typedef struct _MMModemGsmNetwork MMModemGsmNetwork; + +typedef void (*MMModemGsmNetworkScanFn) (MMModemGsmNetwork *self, + GPtrArray *results, + GError *error, + gpointer user_data); + +typedef void (*MMModemGsmNetworkRegInfoFn) (MMModemGsmNetwork *self, + MMModemGsmNetworkRegStatus status, + const char *oper_code, + const char *oper_name, + GError *error, + gpointer user_data); + +struct _MMModemGsmNetwork { + GTypeInterface g_iface; + + /* Methods */ + /* 'register' is a reserved word */ + void (*do_register) (MMModemGsmNetwork *self, + const char *network_id, + MMModemFn callback, + gpointer user_data); + + void (*scan) (MMModemGsmNetwork *self, + MMModemGsmNetworkScanFn callback, + gpointer user_data); + + void (*set_apn) (MMModemGsmNetwork *self, + const char *apn, + MMModemFn callback, + gpointer user_data); + + void (*get_signal_quality) (MMModemGsmNetwork *self, + MMModemUIntFn callback, + gpointer user_data); + + void (*set_band) (MMModemGsmNetwork *self, + MMModemGsmBand band, + MMModemFn callback, + gpointer user_data); + + void (*get_band) (MMModemGsmNetwork *self, + MMModemUIntFn callback, + gpointer user_data); + + void (*set_network_mode) (MMModemGsmNetwork *self, + MMModemGsmMode mode, + MMModemFn callback, + gpointer user_data); + + void (*get_network_mode) (MMModemGsmNetwork *self, + MMModemUIntFn callback, + gpointer user_data); + + void (*get_registration_info) (MMModemGsmNetwork *self, + MMModemGsmNetworkRegInfoFn callback, + gpointer user_data); + + /* Signals */ + void (*signal_quality) (MMModemGsmNetwork *self, + guint32 quality); + + void (*registration_info) (MMModemGsmNetwork *self, + MMModemGsmNetworkRegStatus status, + const char *open_code, + const char *oper_name); + + void (*network_mode) (MMModemGsmNetwork *self, + MMModemGsmMode mode); +}; + +GType mm_modem_gsm_network_get_type (void); + +void mm_modem_gsm_network_register (MMModemGsmNetwork *self, + const char *network_id, + MMModemFn callback, + gpointer user_data); + +void mm_modem_gsm_network_scan (MMModemGsmNetwork *self, + MMModemGsmNetworkScanFn callback, + gpointer user_data); + +void mm_modem_gsm_network_set_apn (MMModemGsmNetwork *self, + const char *apn, + MMModemFn callback, + gpointer user_data); + +void mm_modem_gsm_network_get_signal_quality (MMModemGsmNetwork *self, + MMModemUIntFn callback, + gpointer user_data); + +void mm_modem_gsm_network_set_band (MMModemGsmNetwork *self, + MMModemGsmBand band, + MMModemFn callback, + gpointer user_data); + +void mm_modem_gsm_network_get_band (MMModemGsmNetwork *self, + MMModemUIntFn callback, + gpointer user_data); + +void mm_modem_gsm_network_set_mode (MMModemGsmNetwork *self, + MMModemGsmMode mode, + MMModemFn callback, + gpointer user_data); + +void mm_modem_gsm_network_get_mode (MMModemGsmNetwork *self, + MMModemUIntFn callback, + gpointer user_data); + +void mm_modem_gsm_network_get_registration_info (MMModemGsmNetwork *self, + MMModemGsmNetworkRegInfoFn callback, + gpointer user_data); + +/* Protected */ + +void mm_modem_gsm_network_signal_quality (MMModemGsmNetwork *self, + guint32 quality); + +void mm_modem_gsm_network_registration_info (MMModemGsmNetwork *self, + MMModemGsmNetworkRegStatus status, + const char *oper_code, + const char *oper_name); + +void mm_modem_gsm_network_mode (MMModemGsmNetwork *self, + MMModemGsmMode mode); + +#endif /* MM_MODEM_GSM_NETWORK_H */ diff --git a/src/mm-modem-gsm-sms.c b/src/mm-modem-gsm-sms.c new file mode 100644 index 0000000..e8ec074 --- /dev/null +++ b/src/mm-modem-gsm-sms.c @@ -0,0 +1,320 @@ +/* -*- 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) 2009 Novell, Inc. + */ + +#include <string.h> +#include <dbus/dbus-glib.h> + +#include "mm-modem-gsm-sms.h" +#include "mm-errors.h" +#include "mm-callback-info.h" +#include "mm-marshal.h" + +static void impl_gsm_modem_sms_delete (MMModemGsmSms *modem, + guint idx, + DBusGMethodInvocation *context); + +static void impl_gsm_modem_sms_get (MMModemGsmSms *modem, + guint idx, + DBusGMethodInvocation *context); + +static void impl_gsm_modem_sms_get_format (MMModemGsmSms *modem, + DBusGMethodInvocation *context); + +static void impl_gsm_modem_sms_set_format (MMModemGsmSms *modem, + guint format, + DBusGMethodInvocation *context); + +static void impl_gsm_modem_sms_get_smsc (MMModemGsmSms *modem, + DBusGMethodInvocation *context); + +static void impl_gsm_modem_sms_set_smsc (MMModemGsmSms *modem, + const char *smsc, + DBusGMethodInvocation *context); + +static void impl_gsm_modem_sms_list (MMModemGsmSms *modem, + DBusGMethodInvocation *context); + +static void impl_gsm_modem_sms_save (MMModemGsmSms *modem, + GHashTable *properties, + DBusGMethodInvocation *context); + +static void impl_gsm_modem_sms_send (MMModemGsmSms *modem, + GHashTable *properties, + DBusGMethodInvocation *context); + +static void impl_gsm_modem_sms_send_from_storage (MMModemGsmSms *modem, + guint idx, + DBusGMethodInvocation *context); + +static void impl_gsm_modem_sms_set_indication (MMModemGsmSms *modem, + guint mode, + guint mt, + guint bm, + guint ds, + guint bfr, + DBusGMethodInvocation *context); + +#include "mm-modem-gsm-sms-glue.h" + +enum { + SMS_RECEIVED, + COMPLETED, + + LAST_SIGNAL +}; + +static guint signals[LAST_SIGNAL] = { 0 }; + +/*****************************************************************************/ + +static void +async_call_done (MMModem *modem, GError *error, gpointer user_data) +{ + DBusGMethodInvocation *context = (DBusGMethodInvocation *) user_data; + + if (error) + dbus_g_method_return_error (context, error); + else + dbus_g_method_return (context); +} + +static void +async_call_not_supported (MMModemGsmSms *self, + MMModemFn callback, + gpointer user_data) +{ + MMCallbackInfo *info; + + info = mm_callback_info_new (MM_MODEM (self), callback, user_data); + info->error = g_error_new_literal (MM_MODEM_ERROR, MM_MODEM_ERROR_OPERATION_NOT_SUPPORTED, + "Operation not supported"); + mm_callback_info_schedule (info); +} + +/*****************************************************************************/ + +void +mm_modem_gsm_sms_send (MMModemGsmSms *self, + const char *number, + const char *text, + const char *smsc, + guint validity, + guint class, + MMModemFn callback, + gpointer user_data) +{ + g_return_if_fail (MM_IS_MODEM_GSM_SMS (self)); + g_return_if_fail (number != NULL); + g_return_if_fail (text != NULL); + g_return_if_fail (callback != NULL); + + if (MM_MODEM_GSM_SMS_GET_INTERFACE (self)->send) + MM_MODEM_GSM_SMS_GET_INTERFACE (self)->send (self, number, text, smsc, validity, class, callback, user_data); + else + async_call_not_supported (self, callback, user_data); + +} + +/*****************************************************************************/ + +static void +impl_gsm_modem_sms_delete (MMModemGsmSms *modem, + guint idx, + DBusGMethodInvocation *context) +{ + async_call_not_supported (modem, async_call_done, context); +} + +static void +impl_gsm_modem_sms_get (MMModemGsmSms *modem, + guint idx, + DBusGMethodInvocation *context) +{ + async_call_not_supported (modem, async_call_done, context); +} + +static void +impl_gsm_modem_sms_get_format (MMModemGsmSms *modem, + DBusGMethodInvocation *context) +{ + async_call_not_supported (modem, async_call_done, context); +} + +static void +impl_gsm_modem_sms_set_format (MMModemGsmSms *modem, + guint format, + DBusGMethodInvocation *context) +{ + async_call_not_supported (modem, async_call_done, context); +} + +static void +impl_gsm_modem_sms_get_smsc (MMModemGsmSms *modem, + DBusGMethodInvocation *context) +{ + async_call_not_supported (modem, async_call_done, context); +} + +static void +impl_gsm_modem_sms_set_smsc (MMModemGsmSms *modem, + const char *smsc, + DBusGMethodInvocation *context) +{ + async_call_not_supported (modem, async_call_done, context); +} + +static void +impl_gsm_modem_sms_list (MMModemGsmSms *modem, + DBusGMethodInvocation *context) +{ + async_call_not_supported (modem, async_call_done, context); +} + +static void +impl_gsm_modem_sms_save (MMModemGsmSms *modem, + GHashTable *properties, + DBusGMethodInvocation *context) +{ + async_call_not_supported (modem, async_call_done, context); +} + +static void +impl_gsm_modem_sms_send (MMModemGsmSms *modem, + GHashTable *properties, + DBusGMethodInvocation *context) +{ + GValue *value; + const char *number = NULL; + const char *text = NULL ; + const char *smsc = NULL; + GError *error = NULL; + guint validity = 0; + guint class = 0; + + value = (GValue *) g_hash_table_lookup (properties, "number"); + if (value) + number = g_value_get_string (value); + + value = (GValue *) g_hash_table_lookup (properties, "text"); + if (value) + text = g_value_get_string (value); + + value = (GValue *) g_hash_table_lookup (properties, "smsc"); + if (value) + smsc = g_value_get_string (value); + + value = (GValue *) g_hash_table_lookup (properties, "validity"); + if (value) + validity = g_value_get_uint (value); + + value = (GValue *) g_hash_table_lookup (properties, "class"); + if (value) + class = g_value_get_uint (value); + + if (!number) + error = g_error_new_literal (MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL, + "Missing number"); + else if (!text) + error = g_error_new_literal (MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL, + "Missing message text"); + + if (error) { + async_call_done (MM_MODEM (modem), error, context); + g_error_free (error); + } else + mm_modem_gsm_sms_send (modem, number, text, smsc, validity, class, async_call_done, context); +} + +static void +impl_gsm_modem_sms_send_from_storage (MMModemGsmSms *modem, + guint idx, + DBusGMethodInvocation *context) +{ + async_call_not_supported (modem, async_call_done, context); +} + +static void +impl_gsm_modem_sms_set_indication (MMModemGsmSms *modem, + guint mode, + guint mt, + guint bm, + guint ds, + guint bfr, + DBusGMethodInvocation *context) +{ + async_call_not_supported (modem, async_call_done, context); +} + +/*****************************************************************************/ + +static void +mm_modem_gsm_sms_init (gpointer g_iface) +{ + GType iface_type = G_TYPE_FROM_INTERFACE (g_iface); + static gboolean initialized = FALSE; + + if (initialized) + return; + + /* Signals */ + signals[SMS_RECEIVED] = + g_signal_new ("sms-received", + iface_type, + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (MMModemGsmSms, sms_received), + NULL, NULL, + mm_marshal_VOID__UINT_BOOLEAN, + G_TYPE_NONE, 2, G_TYPE_UINT, G_TYPE_BOOLEAN); + + signals[COMPLETED] = + g_signal_new ("completed", + iface_type, + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (MMModemGsmSms, completed), + NULL, NULL, + mm_marshal_VOID__UINT_BOOLEAN, + G_TYPE_NONE, 2, G_TYPE_UINT, G_TYPE_BOOLEAN); + + initialized = TRUE; +} + +GType +mm_modem_gsm_sms_get_type (void) +{ + static GType sms_type = 0; + + if (!G_UNLIKELY (sms_type)) { + const GTypeInfo sms_info = { + sizeof (MMModemGsmSms), /* class_size */ + mm_modem_gsm_sms_init, /* base_init */ + NULL, /* base_finalize */ + NULL, + NULL, /* class_finalize */ + NULL, /* class_data */ + 0, + 0, /* n_preallocs */ + NULL + }; + + sms_type = g_type_register_static (G_TYPE_INTERFACE, + "MMModemGsmSms", + &sms_info, 0); + + g_type_interface_add_prerequisite (sms_type, G_TYPE_OBJECT); + dbus_g_object_type_install_info (sms_type, &dbus_glib_mm_modem_gsm_sms_object_info); + } + + return sms_type; +} diff --git a/src/mm-modem-gsm-sms.h b/src/mm-modem-gsm-sms.h new file mode 100644 index 0000000..79a5bb0 --- /dev/null +++ b/src/mm-modem-gsm-sms.h @@ -0,0 +1,62 @@ +/* -*- 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) 2009 Novell, Inc. + */ + +#ifndef MM_MODEM_GSM_SMS_H +#define MM_MODEM_GSM_SMS_H + +#include <mm-modem.h> + +#define MM_TYPE_MODEM_GSM_SMS (mm_modem_gsm_sms_get_type ()) +#define MM_MODEM_GSM_SMS(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_MODEM_GSM_SMS, MMModemGsmSms)) +#define MM_IS_MODEM_GSM_SMS(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_MODEM_GSM_SMS)) +#define MM_MODEM_GSM_SMS_GET_INTERFACE(obj) (G_TYPE_INSTANCE_GET_INTERFACE ((obj), MM_TYPE_MODEM_GSM_SMS, MMModemGsmSms)) + +typedef struct _MMModemGsmSms MMModemGsmSms; + +struct _MMModemGsmSms { + GTypeInterface g_iface; + + /* Methods */ + void (*send) (MMModemGsmSms *modem, + const char *number, + const char *text, + const char *smsc, + guint validity, + guint class, + MMModemFn callback, + gpointer user_data); + + /* Signals */ + void (*sms_received) (MMModemGsmSms *self, + guint32 index, + gboolean completed); + + void (*completed) (MMModemGsmSms *self, + guint32 index, + gboolean completed); +}; + +GType mm_modem_gsm_sms_get_type (void); + +void mm_modem_gsm_sms_send (MMModemGsmSms *self, + const char *number, + const char *text, + const char *smsc, + guint validity, + guint class, + MMModemFn callback, + gpointer user_data); + +#endif /* MM_MODEM_GSM_SMS_H */ diff --git a/src/mm-modem-gsm.h b/src/mm-modem-gsm.h new file mode 100644 index 0000000..852ff85 --- /dev/null +++ b/src/mm-modem-gsm.h @@ -0,0 +1,57 @@ +/* -*- 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_GSM_H +#define MM_MODEM_GSM_H + +typedef enum { + MM_MODEM_GSM_MODE_UNKNOWN = 0x00000000, + MM_MODEM_GSM_MODE_ANY = 0x00000001, + MM_MODEM_GSM_MODE_GPRS = 0x00000002, + MM_MODEM_GSM_MODE_EDGE = 0x00000004, + MM_MODEM_GSM_MODE_UMTS = 0x00000008, + MM_MODEM_GSM_MODE_HSDPA = 0x00000010, + MM_MODEM_GSM_MODE_2G_PREFERRED = 0x00000020, + MM_MODEM_GSM_MODE_3G_PREFERRED = 0x00000040, + MM_MODEM_GSM_MODE_2G_ONLY = 0x00000080, + MM_MODEM_GSM_MODE_3G_ONLY = 0x00000100, + MM_MODEM_GSM_MODE_HSUPA = 0x00000200, + MM_MODEM_GSM_MODE_HSPA = 0x00000400, + + MM_MODEM_GSM_MODE_LAST = MM_MODEM_GSM_MODE_HSPA +} MMModemGsmMode; + +typedef enum { + MM_MODEM_GSM_BAND_UNKNOWN = 0x00000000, + MM_MODEM_GSM_BAND_ANY = 0x00000001, + MM_MODEM_GSM_BAND_EGSM = 0x00000002, /* 900 MHz */ + MM_MODEM_GSM_BAND_DCS = 0x00000004, /* 1800 MHz */ + MM_MODEM_GSM_BAND_PCS = 0x00000008, /* 1900 MHz */ + MM_MODEM_GSM_BAND_G850 = 0x00000010, /* 850 MHz */ + MM_MODEM_GSM_BAND_U2100 = 0x00000020, /* WCDMA 3GPP UMTS 2100 MHz (Class I) */ + MM_MODEM_GSM_BAND_U1800 = 0x00000040, /* WCDMA 3GPP UMTS 1800 MHz (Class III) */ + MM_MODEM_GSM_BAND_U17IV = 0x00000080, /* WCDMA 3GPP AWS 1700/2100 MHz (Class IV) */ + MM_MODEM_GSM_BAND_U800 = 0x00000100, /* WCDMA 3GPP UMTS 800 MHz (Class VI) */ + MM_MODEM_GSM_BAND_U850 = 0x00000200, /* WCDMA 3GPP UMTS 850 MHz (Class V) */ + MM_MODEM_GSM_BAND_U900 = 0x00000400, /* WCDMA 3GPP UMTS 900 MHz (Class VIII) */ + MM_MODEM_GSM_BAND_U17IX = 0x00000800, /* WCDMA 3GPP UMTS 1700 MHz (Class IX) */ + + MM_MODEM_GSM_BAND_LAST = MM_MODEM_GSM_BAND_U17IX +} MMModemGsmBand; + + +#endif /* MM_MODEM_GSM_H */ + diff --git a/src/mm-modem-helpers.c b/src/mm-modem-helpers.c new file mode 100644 index 0000000..1741b5f --- /dev/null +++ b/src/mm-modem-helpers.c @@ -0,0 +1,203 @@ +/* -*- 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 <glib.h> +#include <string.h> +#include <ctype.h> + +#include "mm-errors.h" +#include "mm-modem-helpers.h" + +static void +save_scan_value (GHashTable *hash, const char *key, GMatchInfo *info, guint32 num) +{ + char *quoted; + size_t len; + + g_return_if_fail (info != NULL); + + quoted = g_match_info_fetch (info, num); + if (!quoted) + return; + + len = strlen (quoted); + + /* Unquote the item if needed */ + if ((len >= 2) && (quoted[0] == '"') && (quoted[len - 1] == '"')) { + quoted[0] = ' '; + quoted[len - 1] = ' '; + quoted = g_strstrip (quoted); + } + + if (!strlen (quoted)) { + g_free (quoted); + return; + } + + g_hash_table_insert (hash, g_strdup (key), quoted); +} + +/* If the response was successfully parsed (even if no valid entries were + * found) the pointer array will be returned. + */ +GPtrArray * +mm_gsm_parse_scan_response (const char *reply, GError **error) +{ + /* Got valid reply */ + GPtrArray *results = NULL; + GRegex *r; + GMatchInfo *match_info; + GError *err = NULL; + gboolean umts_format = TRUE; + + g_return_val_if_fail (reply != NULL, NULL); + if (error) + g_return_val_if_fail (*error == NULL, NULL); + + if (!strstr (reply, "+COPS: ")) { + g_set_error_literal (error, + MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL, + "Could not parse scan results."); + return NULL; + } + + reply = strstr (reply, "+COPS: ") + 7; + + /* Cell access technology (GSM, UTRAN, etc) got added later and not all + * modems implement it. Some modesm have quirks that make it hard to + * use one regular experession for matching both pre-UMTS and UMTS + * responses. So try UMTS-format first and fall back to pre-UMTS if + * we get no UMTS-formst matches. + */ + + /* Quirk: Sony-Ericsson TM-506 sometimes includes a stray ')' like so, + * which is what makes it hard to match both pre-UMTS and UMTS in + * the same regex: + * + * +COPS: (2,"","T-Mobile","31026",0),(1,"AT&T","AT&T","310410"),0) + */ + + r = g_regex_new ("\\((\\d),([^,\\)]*),([^,\\)]*),([^,\\)]*)[\\)]?,(\\d)\\)", G_REGEX_UNGREEDY, 0, NULL); + if (err) { + g_error ("Invalid regular expression: %s", err->message); + g_error_free (err); + g_set_error_literal (error, + MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL, + "Could not parse scan results."); + return NULL; + } + + /* If we didn't get any hits, try the pre-UMTS format match */ + if (!g_regex_match (r, reply, 0, &match_info)) { + g_regex_unref (r); + if (match_info) { + g_match_info_free (match_info); + match_info = NULL; + } + + /* Pre-UMTS format doesn't include the cell access technology after + * the numeric operator element. + * + * Ex: Motorola C-series (BUSlink SCWi275u) like so: + * + * +COPS: (2,"T-Mobile","","310260"),(0,"Cingular Wireless","","310410") + */ + + /* Quirk: Some Nokia phones (N80) don't send the quotes for empty values: + * + * +COPS: (2,"T - Mobile",,"31026"),(1,"Einstein PCS",,"31064"),(1,"Cingular",,"31041"),,(0,1,3),(0,2) + */ + + r = g_regex_new ("\\((\\d),([^,\\)]*),([^,\\)]*),([^\\)]*)\\)", G_REGEX_UNGREEDY, 0, NULL); + if (err) { + g_error ("Invalid regular expression: %s", err->message); + g_error_free (err); + g_set_error_literal (error, + MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL, + "Could not parse scan results."); + return NULL; + } + + g_regex_match (r, reply, 0, &match_info); + umts_format = FALSE; + } + + /* Parse the results */ + results = g_ptr_array_new (); + while (g_match_info_matches (match_info)) { + GHashTable *hash; + char *access_tech = NULL; + const char *tmp; + gboolean valid = FALSE; + + hash = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free); + + save_scan_value (hash, MM_SCAN_TAG_STATUS, match_info, 1); + save_scan_value (hash, MM_SCAN_TAG_OPER_LONG, match_info, 2); + save_scan_value (hash, MM_SCAN_TAG_OPER_SHORT, match_info, 3); + save_scan_value (hash, MM_SCAN_TAG_OPER_NUM, match_info, 4); + + /* Only try for access technology with UMTS-format matches */ + if (umts_format) + access_tech = g_match_info_fetch (match_info, 5); + if (access_tech && (strlen (access_tech) == 1)) { + /* Recognized access technologies are between '0' and '6' inclusive... */ + if ((access_tech[0] >= '0') && (access_tech[0] <= '6')) + g_hash_table_insert (hash, g_strdup (MM_SCAN_TAG_ACCESS_TECH), access_tech); + } else + g_free (access_tech); + + /* If the operator number isn't valid (ie, at least 5 digits), + * ignore the scan result; it's probably the parameter stuff at the + * end of the +COPS response. The regex will sometimes catch this + * but there's no good way to ignore it. + */ + tmp = g_hash_table_lookup (hash, MM_SCAN_TAG_OPER_NUM); + if (tmp && (strlen (tmp) >= 5)) { + valid = TRUE; + while (*tmp) { + if (!isdigit (*tmp) && (*tmp != '-')) { + valid = FALSE; + break; + } + tmp++; + } + + if (valid) + g_ptr_array_add (results, hash); + } + + if (!valid) + g_hash_table_destroy (hash); + + g_match_info_next (match_info, NULL); + } + + g_match_info_free (match_info); + g_regex_unref (r); + + return results; +} + +void +mm_gsm_destroy_scan_data (gpointer data) +{ + GPtrArray *results = (GPtrArray *) data; + + g_ptr_array_foreach (results, (GFunc) g_hash_table_destroy, NULL); + g_ptr_array_free (results, TRUE); +} + diff --git a/src/mm-modem-helpers.h b/src/mm-modem-helpers.h new file mode 100644 index 0000000..ddc9cbc --- /dev/null +++ b/src/mm-modem-helpers.h @@ -0,0 +1,31 @@ +/* -*- 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_HELPERS_H +#define MM_MODEM_HELPERS_H + +#define MM_SCAN_TAG_STATUS "status" +#define MM_SCAN_TAG_OPER_LONG "operator-long" +#define MM_SCAN_TAG_OPER_SHORT "operator-short" +#define MM_SCAN_TAG_OPER_NUM "operator-num" +#define MM_SCAN_TAG_ACCESS_TECH "access-tech" + +GPtrArray *mm_gsm_parse_scan_response (const char *reply, GError **error); + +void mm_gsm_destroy_scan_data (gpointer data); + +#endif /* MM_MODEM_HELPERS_H */ + diff --git a/src/mm-modem-simple.c b/src/mm-modem-simple.c new file mode 100644 index 0000000..415fd44 --- /dev/null +++ b/src/mm-modem-simple.c @@ -0,0 +1,156 @@ +/* -*- 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) 2009 Novell, Inc. + * Copyright (C) 2009 Red Hat, Inc. + */ + +#include <dbus/dbus-glib.h> + +#include "mm-modem-simple.h" +#include "mm-errors.h" +#include "mm-callback-info.h" + +static void impl_modem_simple_connect (MMModemSimple *self, GHashTable *properties, DBusGMethodInvocation *context); +static void impl_modem_simple_get_status (MMModemSimple *self, DBusGMethodInvocation *context); + +#include "mm-modem-simple-glue.h" + +void +mm_modem_simple_connect (MMModemSimple *self, + GHashTable *properties, + MMModemFn callback, + gpointer user_data) +{ + g_return_if_fail (MM_IS_MODEM_SIMPLE (self)); + g_return_if_fail (properties != NULL); + + if (MM_MODEM_SIMPLE_GET_INTERFACE (self)->connect) + MM_MODEM_SIMPLE_GET_INTERFACE (self)->connect (self, properties, callback, user_data); + else { + MMCallbackInfo *info; + + info = mm_callback_info_new (MM_MODEM (self), callback, user_data); + info->error = g_error_new_literal (MM_MODEM_ERROR, MM_MODEM_ERROR_OPERATION_NOT_SUPPORTED, + "Operation not supported"); + mm_callback_info_schedule (info); + } +} + +static void +simple_get_status_invoke (MMCallbackInfo *info) +{ + MMModemSimpleGetStatusFn callback = (MMModemSimpleGetStatusFn) info->callback; + + callback (MM_MODEM_SIMPLE (info->modem), NULL, info->error, info->user_data); +} + +void +mm_modem_simple_get_status (MMModemSimple *self, + MMModemSimpleGetStatusFn callback, + gpointer user_data) +{ + g_return_if_fail (MM_IS_MODEM_SIMPLE (self)); + + if (MM_MODEM_SIMPLE_GET_INTERFACE (self)->get_status) + MM_MODEM_SIMPLE_GET_INTERFACE (self)->get_status (self, callback, user_data); + else { + MMCallbackInfo *info; + + info = mm_callback_info_new_full (MM_MODEM (self), + simple_get_status_invoke, + G_CALLBACK (callback), + user_data); + + info->error = g_error_new_literal (MM_MODEM_ERROR, MM_MODEM_ERROR_OPERATION_NOT_SUPPORTED, + "Operation not supported"); + mm_callback_info_schedule (info); + } +} + +/*****************************************************************************/ + +static void +async_call_done (MMModem *modem, GError *error, gpointer user_data) +{ + DBusGMethodInvocation *context = (DBusGMethodInvocation *) user_data; + + if (error) + dbus_g_method_return_error (context, error); + else + dbus_g_method_return (context); +} + +static void +impl_modem_simple_connect (MMModemSimple *self, + GHashTable *properties, + DBusGMethodInvocation *context) +{ + mm_modem_simple_connect (self, properties, async_call_done, context); +} + +static void +get_status_done (MMModemSimple *modem, + GHashTable *properties, + GError *error, + gpointer user_data) +{ + DBusGMethodInvocation *context = (DBusGMethodInvocation *) user_data; + + if (error) + dbus_g_method_return_error (context, error); + else + dbus_g_method_return (context, properties); +} + +static void +impl_modem_simple_get_status (MMModemSimple *self, + DBusGMethodInvocation *context) +{ + mm_modem_simple_get_status (self, get_status_done, context); +} + +/*****************************************************************************/ + +static void +mm_modem_simple_init (gpointer g_iface) +{ +} + +GType +mm_modem_simple_get_type (void) +{ + static GType modem_type = 0; + + if (!G_UNLIKELY (modem_type)) { + const GTypeInfo modem_info = { + sizeof (MMModemSimple), /* class_size */ + mm_modem_simple_init, /* base_init */ + NULL, /* base_finalize */ + NULL, + NULL, /* class_finalize */ + NULL, /* class_data */ + 0, + 0, /* n_preallocs */ + NULL + }; + + modem_type = g_type_register_static (G_TYPE_INTERFACE, + "MMModemSimple", + &modem_info, 0); + + g_type_interface_add_prerequisite (modem_type, G_TYPE_OBJECT); + dbus_g_object_type_install_info (modem_type, &dbus_glib_mm_modem_simple_object_info); + } + + return modem_type; +} diff --git a/src/mm-modem-simple.h b/src/mm-modem-simple.h new file mode 100644 index 0000000..03b5561 --- /dev/null +++ b/src/mm-modem-simple.h @@ -0,0 +1,59 @@ +/* -*- 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) 2009 Novell, Inc. + */ + +#ifndef MM_MODEM_SIMPLE_H +#define MM_MODEM_SIMPLE_H + +#include <glib-object.h> +#include <mm-modem.h> + +#define MM_TYPE_MODEM_SIMPLE (mm_modem_simple_get_type ()) +#define MM_MODEM_SIMPLE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_MODEM_SIMPLE, MMModemSimple)) +#define MM_IS_MODEM_SIMPLE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_MODEM_SIMPLE)) +#define MM_MODEM_SIMPLE_GET_INTERFACE(obj) (G_TYPE_INSTANCE_GET_INTERFACE ((obj), MM_TYPE_MODEM_SIMPLE, MMModemSimple)) + +typedef struct _MMModemSimple MMModemSimple; + +typedef void (*MMModemSimpleGetStatusFn) (MMModemSimple *modem, + GHashTable *properties, + GError *error, + gpointer user_data); + +struct _MMModemSimple { + GTypeInterface g_iface; + + /* Methods */ + void (*connect) (MMModemSimple *self, + GHashTable *properties, + MMModemFn callback, + gpointer user_data); + + void (*get_status) (MMModemSimple *self, + MMModemSimpleGetStatusFn callback, + gpointer user_data); +}; + +GType mm_modem_simple_get_type (void); + +void mm_modem_simple_connect (MMModemSimple *self, + GHashTable *properties, + MMModemFn callback, + gpointer user_data); + +void mm_modem_simple_get_status (MMModemSimple *self, + MMModemSimpleGetStatusFn callback, + gpointer user_data); + +#endif /* MM_MODEM_SIMPLE_H */ diff --git a/src/mm-modem.c b/src/mm-modem.c new file mode 100644 index 0000000..a65d883 --- /dev/null +++ b/src/mm-modem.c @@ -0,0 +1,738 @@ +/* -*- 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 <dbus/dbus-glib.h> +#include "mm-modem.h" +#include "mm-errors.h" +#include "mm-callback-info.h" +#include "mm-marshal.h" + +static void impl_modem_enable (MMModem *modem, gboolean enable, DBusGMethodInvocation *context); +static void impl_modem_connect (MMModem *modem, const char *number, DBusGMethodInvocation *context); +static void impl_modem_disconnect (MMModem *modem, DBusGMethodInvocation *context); +static void impl_modem_get_ip4_config (MMModem *modem, DBusGMethodInvocation *context); +static void impl_modem_get_info (MMModem *modem, DBusGMethodInvocation *context); + +#include "mm-modem-glue.h" + +/* Should be used from callbacks to check whether the modem was removed after + * the callback's operation was started, but before the callback itself was + * called, in which case the MMModem passed to the callback is NULL. + */ +GError * +mm_modem_check_removed (MMModem *self, const GError *error) +{ + if (!self) { + /* If the modem was NULL, the error *should* have been + * MM_MODEM_ERROR_REMOVED. If it wasn't, make it that. + */ + return g_error_new_literal (MM_MODEM_ERROR, + MM_MODEM_ERROR_REMOVED, + "The modem was removed."); + } + + return error ? g_error_copy (error) : NULL; +} + +static void +async_op_not_supported (MMModem *self, + MMModemFn callback, + gpointer user_data) +{ + MMCallbackInfo *info; + + info = mm_callback_info_new (self, callback, user_data); + info->error = g_error_new_literal (MM_MODEM_ERROR, MM_MODEM_ERROR_OPERATION_NOT_SUPPORTED, + "Operation not supported"); + mm_callback_info_schedule (info); +} + +static void +async_call_done (MMModem *modem, GError *error, gpointer user_data) +{ + DBusGMethodInvocation *context = (DBusGMethodInvocation *) user_data; + + if (error) + dbus_g_method_return_error (context, error); + else + dbus_g_method_return (context); +} + +void +mm_modem_enable (MMModem *self, + MMModemFn callback, + gpointer user_data) +{ + MMModemState state; + + g_return_if_fail (MM_IS_MODEM (self)); + g_return_if_fail (callback != NULL); + + state = mm_modem_get_state (self); + if (state >= MM_MODEM_STATE_ENABLED) { + MMCallbackInfo *info; + + info = mm_callback_info_new (self, callback, user_data); + + if (state == MM_MODEM_STATE_ENABLING) { + info->error = g_error_new_literal (MM_MODEM_ERROR, + MM_MODEM_ERROR_OPERATION_IN_PROGRESS, + "The device is already being enabled."); + } else { + /* Already enabled */ + } + + mm_callback_info_schedule (info); + return; + } + + if (MM_MODEM_GET_INTERFACE (self)->enable) + MM_MODEM_GET_INTERFACE (self)->enable (self, callback, user_data); + else + async_op_not_supported (self, callback, user_data); +} + +static void +finish_disable (MMModem *self, + MMModemFn callback, + gpointer user_data) +{ + if (MM_MODEM_GET_INTERFACE (self)->disable) + MM_MODEM_GET_INTERFACE (self)->disable (self, callback, user_data); + else + async_op_not_supported (self, callback, user_data); +} + +typedef struct { + MMModemFn callback; + gpointer user_data; +} DisableDisconnectInfo; + +static void +disable_disconnect_done (MMModem *self, + GError *error, + gpointer user_data) +{ + DisableDisconnectInfo *cb_data = user_data; + GError *tmp_error = NULL; + + /* Check for modem removal */ + if (g_error_matches (error, MM_MODEM_ERROR, MM_MODEM_ERROR_REMOVED)) + tmp_error = g_error_copy (error); + else if (!self) { + tmp_error = g_error_new_literal (MM_MODEM_ERROR, + MM_MODEM_ERROR_REMOVED, + "The modem was removed."); + } + + /* And send an immediate error reply if the modem was removed */ + if (tmp_error) { + cb_data->callback (NULL, tmp_error, cb_data->user_data); + g_free (cb_data); + g_error_free (tmp_error); + return; + } + + if (error) { + /* Don't really care what the error was; log it and proceed to disable */ + g_warning ("%s: (%s): error disconnecting the modem while disabling: (%d) %s", + __func__, + mm_modem_get_device (self), + error ? error->code : -1, + error && error->message ? error->message : "(unknown)"); + } + finish_disable (self, cb_data->callback, cb_data->user_data); + g_free (cb_data); +} + +void +mm_modem_disable (MMModem *self, + MMModemFn callback, + gpointer user_data) +{ + MMModemState state; + + g_return_if_fail (MM_IS_MODEM (self)); + g_return_if_fail (callback != NULL); + + state = mm_modem_get_state (self); + if (state <= MM_MODEM_STATE_DISABLING) { + MMCallbackInfo *info; + + info = mm_callback_info_new (self, callback, user_data); + + if (state == MM_MODEM_STATE_DISABLING) { + info->error = g_error_new_literal (MM_MODEM_ERROR, + MM_MODEM_ERROR_OPERATION_IN_PROGRESS, + "The device is already being disabled."); + } else { + /* Already disabled */ + } + + mm_callback_info_schedule (info); + return; + } + + /* If the modem is connected, disconnect it */ + if (state >= MM_MODEM_STATE_CONNECTING) { + DisableDisconnectInfo *cb_data; + + cb_data = g_malloc0 (sizeof (DisableDisconnectInfo)); + cb_data->callback = callback; + cb_data->user_data = user_data; + mm_modem_disconnect (self, disable_disconnect_done, cb_data); + } else + finish_disable (self, callback, user_data); +} + +static void +impl_modem_enable (MMModem *modem, + gboolean enable, + DBusGMethodInvocation *context) +{ + if (enable) + mm_modem_enable (modem, async_call_done, context); + else + mm_modem_disable (modem, async_call_done, context); +} + +void +mm_modem_connect (MMModem *self, + const char *number, + MMModemFn callback, + gpointer user_data) +{ + MMModemState state; + + g_return_if_fail (MM_IS_MODEM (self)); + g_return_if_fail (callback != NULL); + g_return_if_fail (number != NULL); + + state = mm_modem_get_state (self); + if (state >= MM_MODEM_STATE_CONNECTING) { + MMCallbackInfo *info; + + /* Already connecting */ + info = mm_callback_info_new (self, callback, user_data); + if (state == MM_MODEM_STATE_CONNECTING) { + info->error = g_error_new_literal (MM_MODEM_ERROR, + MM_MODEM_ERROR_OPERATION_IN_PROGRESS, + "The device is already being connected."); + } else { + /* already connected */ + } + + mm_callback_info_schedule (info); + return; + } + + if (MM_MODEM_GET_INTERFACE (self)->connect) + MM_MODEM_GET_INTERFACE (self)->connect (self, number, callback, user_data); + else + async_op_not_supported (self, callback, user_data); +} + +static void +impl_modem_connect (MMModem *modem, + const char *number, + DBusGMethodInvocation *context) +{ + mm_modem_connect (modem, number, async_call_done, context); +} + +static void +get_ip4_invoke (MMCallbackInfo *info) +{ + MMModemIp4Fn callback = (MMModemIp4Fn) info->callback; + + callback (info->modem, + GPOINTER_TO_UINT (mm_callback_info_get_data (info, "ip4-address")), + (GArray *) mm_callback_info_get_data (info, "ip4-dns"), + info->error, info->user_data); +} + +void +mm_modem_get_ip4_config (MMModem *self, + MMModemIp4Fn callback, + gpointer user_data) +{ + g_return_if_fail (MM_IS_MODEM (self)); + g_return_if_fail (callback != NULL); + + if (MM_MODEM_GET_INTERFACE (self)->get_ip4_config) + MM_MODEM_GET_INTERFACE (self)->get_ip4_config (self, callback, user_data); + else { + MMCallbackInfo *info; + + info = mm_callback_info_new_full (self, + get_ip4_invoke, + G_CALLBACK (callback), + user_data); + + info->error = g_error_new_literal (MM_MODEM_ERROR, MM_MODEM_ERROR_OPERATION_NOT_SUPPORTED, + "Operation not supported"); + mm_callback_info_schedule (info); + } +} + +static void +value_array_add_uint (GValueArray *array, guint32 i) +{ + GValue value = { 0, }; + + g_value_init (&value, G_TYPE_UINT); + g_value_set_uint (&value, i); + g_value_array_append (array, &value); + g_value_unset (&value); +} + +static void +get_ip4_done (MMModem *modem, + guint32 address, + GArray *dns, + GError *error, + gpointer user_data) +{ + DBusGMethodInvocation *context = (DBusGMethodInvocation *) user_data; + + if (error) + dbus_g_method_return_error (context, error); + else { + GValueArray *array; + guint32 dns1 = 0; + guint32 dns2 = 0; + guint32 dns3 = 0; + + array = g_value_array_new (4); + + if (dns) { + if (dns->len > 0) + + dns1 = g_array_index (dns, guint32, 0); + if (dns->len > 1) + dns2 = g_array_index (dns, guint32, 1); + if (dns->len > 2) + dns3 = g_array_index (dns, guint32, 2); + } + + value_array_add_uint (array, address); + value_array_add_uint (array, dns1); + value_array_add_uint (array, dns2); + value_array_add_uint (array, dns3); + + dbus_g_method_return (context, array); + } +} + +static void +impl_modem_get_ip4_config (MMModem *modem, + DBusGMethodInvocation *context) +{ + mm_modem_get_ip4_config (modem, get_ip4_done, context); +} + +void +mm_modem_disconnect (MMModem *self, + MMModemFn callback, + gpointer user_data) +{ + MMModemState state; + + g_return_if_fail (MM_IS_MODEM (self)); + g_return_if_fail (callback != NULL); + + state = mm_modem_get_state (self); + if (state <= MM_MODEM_STATE_DISCONNECTING) { + MMCallbackInfo *info; + + /* Already connecting */ + info = mm_callback_info_new (self, callback, user_data); + if (state == MM_MODEM_STATE_DISCONNECTING) { + info->error = g_error_new_literal (MM_MODEM_ERROR, + MM_MODEM_ERROR_OPERATION_IN_PROGRESS, + "The device is already being disconnected."); + } else { + /* already disconnected */ + } + + mm_callback_info_schedule (info); + return; + } + + if (MM_MODEM_GET_INTERFACE (self)->disconnect) + MM_MODEM_GET_INTERFACE (self)->disconnect (self, callback, user_data); + else + async_op_not_supported (self, callback, user_data); +} + +static void +impl_modem_disconnect (MMModem *modem, + DBusGMethodInvocation *context) +{ + mm_modem_disconnect (modem, async_call_done, context); +} + +static void +info_call_done (MMModem *self, + const char *manufacturer, + const char *model, + const char *version, + GError *error, + gpointer user_data) +{ + DBusGMethodInvocation *context = (DBusGMethodInvocation *) user_data; + + if (error) + dbus_g_method_return_error (context, error); + else { + GValueArray *array; + GValue value = { 0, }; + + array = g_value_array_new (3); + + /* Manufacturer */ + g_value_init (&value, G_TYPE_STRING); + g_value_set_string (&value, manufacturer); + g_value_array_append (array, &value); + g_value_unset (&value); + + /* Model */ + g_value_init (&value, G_TYPE_STRING); + g_value_set_string (&value, model); + g_value_array_append (array, &value); + g_value_unset (&value); + + /* Version */ + g_value_init (&value, G_TYPE_STRING); + g_value_set_string (&value, version); + g_value_array_append (array, &value); + g_value_unset (&value); + + dbus_g_method_return (context, array); + } +} + +static void +info_invoke (MMCallbackInfo *info) +{ + MMModemInfoFn callback = (MMModemInfoFn) info->callback; + + callback (info->modem, NULL, NULL, NULL, info->error, info->user_data); +} + +static void +info_call_not_supported (MMModem *self, + MMModemInfoFn callback, + gpointer user_data) +{ + MMCallbackInfo *info; + + info = mm_callback_info_new_full (MM_MODEM (self), info_invoke, G_CALLBACK (callback), user_data); + info->error = g_error_new_literal (MM_MODEM_ERROR, MM_MODEM_ERROR_OPERATION_NOT_SUPPORTED, + "Operation not supported"); + + mm_callback_info_schedule (info); +} + +void +mm_modem_get_info (MMModem *self, + MMModemInfoFn callback, + gpointer user_data) +{ + g_return_if_fail (MM_IS_MODEM (self)); + g_return_if_fail (callback != NULL); + + if (MM_MODEM_GET_INTERFACE (self)->get_info) + MM_MODEM_GET_INTERFACE (self)->get_info (self, callback, user_data); + else + info_call_not_supported (self, callback, user_data); +} + +static void +impl_modem_get_info (MMModem *modem, + DBusGMethodInvocation *context) +{ + mm_modem_get_info (modem, info_call_done, context); +} + +/*****************************************************************************/ + +gboolean +mm_modem_owns_port (MMModem *self, + const char *subsys, + const char *name) +{ + g_return_val_if_fail (self != NULL, FALSE); + g_return_val_if_fail (MM_IS_MODEM (self), FALSE); + g_return_val_if_fail (subsys, FALSE); + g_return_val_if_fail (name, FALSE); + + g_assert (MM_MODEM_GET_INTERFACE (self)->owns_port); + return MM_MODEM_GET_INTERFACE (self)->owns_port (self, subsys, name); +} + +gboolean +mm_modem_grab_port (MMModem *self, + const char *subsys, + const char *name, + MMPortType suggested_type, + gpointer user_data, + GError **error) +{ + g_return_val_if_fail (self != NULL, FALSE); + g_return_val_if_fail (MM_IS_MODEM (self), FALSE); + g_return_val_if_fail (subsys, FALSE); + g_return_val_if_fail (name, FALSE); + + g_assert (MM_MODEM_GET_INTERFACE (self)->grab_port); + return MM_MODEM_GET_INTERFACE (self)->grab_port (self, subsys, name, suggested_type, user_data, error); +} + +void +mm_modem_release_port (MMModem *self, + const char *subsys, + const char *name) +{ + g_return_if_fail (self != NULL); + g_return_if_fail (MM_IS_MODEM (self)); + g_return_if_fail (subsys); + g_return_if_fail (name); + + g_assert (MM_MODEM_GET_INTERFACE (self)->release_port); + MM_MODEM_GET_INTERFACE (self)->release_port (self, subsys, name); +} + +gboolean +mm_modem_get_valid (MMModem *self) +{ + gboolean valid = FALSE; + + g_return_val_if_fail (self != NULL, FALSE); + g_return_val_if_fail (MM_IS_MODEM (self), FALSE); + + g_object_get (G_OBJECT (self), MM_MODEM_VALID, &valid, NULL); + return valid; +} + +char * +mm_modem_get_device (MMModem *self) +{ + char *device; + + g_return_val_if_fail (self != NULL, NULL); + g_return_val_if_fail (MM_IS_MODEM (self), NULL); + + g_object_get (G_OBJECT (self), MM_MODEM_MASTER_DEVICE, &device, NULL); + return device; +} + +MMModemState +mm_modem_get_state (MMModem *self) +{ + MMModemState state = MM_MODEM_STATE_UNKNOWN; + + g_object_get (G_OBJECT (self), MM_MODEM_STATE, &state, NULL); + return state; +} + +static const char * +state_to_string (MMModemState state) +{ + switch (state) { + case MM_MODEM_STATE_UNKNOWN: + return "unknown"; + case MM_MODEM_STATE_DISABLED: + return "disabled"; + case MM_MODEM_STATE_DISABLING: + return "disabling"; + case MM_MODEM_STATE_ENABLING: + return "enabling"; + case MM_MODEM_STATE_ENABLED: + return "enabled"; + case MM_MODEM_STATE_SEARCHING: + return "searching"; + case MM_MODEM_STATE_REGISTERED: + return "registered"; + case MM_MODEM_STATE_DISCONNECTING: + return "disconnecting"; + case MM_MODEM_STATE_CONNECTING: + return "connecting"; + case MM_MODEM_STATE_CONNECTED: + return "connected"; + default: + g_assert_not_reached (); + break; + } + + g_assert_not_reached (); + return "(invalid)"; +} + +void +mm_modem_set_state (MMModem *self, + MMModemState new_state, + MMModemStateReason reason) +{ + MMModemState old_state = MM_MODEM_STATE_UNKNOWN; + const char *dbus_path; + + g_object_get (G_OBJECT (self), MM_MODEM_STATE, &old_state, NULL); + + if (new_state != old_state) { + g_object_set (G_OBJECT (self), MM_MODEM_STATE, new_state, NULL); + g_signal_emit_by_name (G_OBJECT (self), "state-changed", new_state, old_state, reason); + + dbus_path = (const char *) g_object_get_data (G_OBJECT (self), DBUS_PATH_TAG); + if (dbus_path) { + g_message ("Modem %s: state changed (%s -> %s)", + dbus_path, + state_to_string (old_state), + state_to_string (new_state)); + } + } +} + +/*****************************************************************************/ + +static void +mm_modem_init (gpointer g_iface) +{ + GType iface_type = G_TYPE_FROM_INTERFACE (g_iface); + static gboolean initialized = FALSE; + + if (initialized) + return; + + /* Properties */ + g_object_interface_install_property + (g_iface, + g_param_spec_string (MM_MODEM_DATA_DEVICE, + "Device", + "Data device", + NULL, + G_PARAM_READWRITE)); + + g_object_interface_install_property + (g_iface, + g_param_spec_string (MM_MODEM_MASTER_DEVICE, + "MasterDevice", + "Master modem parent device of all the modem's ports", + NULL, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); + + g_object_interface_install_property + (g_iface, + g_param_spec_string (MM_MODEM_DRIVER, + "Driver", + "Driver", + NULL, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); + + g_object_interface_install_property + (g_iface, + g_param_spec_string (MM_MODEM_PLUGIN, + "Plugin", + "Plugin name", + NULL, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); + + g_object_interface_install_property + (g_iface, + g_param_spec_uint (MM_MODEM_TYPE, + "Type", + "Type", + 0, G_MAXUINT32, 0, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); + + g_object_interface_install_property + (g_iface, + g_param_spec_uint (MM_MODEM_IP_METHOD, + "IP method", + "IP configuration method", + MM_MODEM_IP_METHOD_PPP, + MM_MODEM_IP_METHOD_DHCP, + MM_MODEM_IP_METHOD_PPP, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); + + g_object_interface_install_property + (g_iface, + g_param_spec_boolean (MM_MODEM_VALID, + "Valid", + "Modem is valid", + FALSE, + G_PARAM_READABLE)); + + g_object_interface_install_property + (g_iface, + g_param_spec_uint (MM_MODEM_STATE, + "State", + "State", + MM_MODEM_STATE_UNKNOWN, + MM_MODEM_STATE_LAST, + MM_MODEM_STATE_UNKNOWN, + G_PARAM_READWRITE)); + + g_object_interface_install_property + (g_iface, + g_param_spec_boolean (MM_MODEM_ENABLED, + "Enabled", + "Modem is enabled", + FALSE, + G_PARAM_READABLE)); + + /* Signals */ + g_signal_new ("state-changed", + iface_type, + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (MMModem, state_changed), + NULL, NULL, + mm_marshal_VOID__UINT_UINT_UINT, + G_TYPE_NONE, 3, + G_TYPE_UINT, G_TYPE_UINT, G_TYPE_UINT); + + initialized = TRUE; +} + +GType +mm_modem_get_type (void) +{ + static GType modem_type = 0; + + if (!G_UNLIKELY (modem_type)) { + const GTypeInfo modem_info = { + sizeof (MMModem), /* class_size */ + mm_modem_init, /* base_init */ + NULL, /* base_finalize */ + NULL, + NULL, /* class_finalize */ + NULL, /* class_data */ + 0, + 0, /* n_preallocs */ + NULL + }; + + modem_type = g_type_register_static (G_TYPE_INTERFACE, + "MMModem", + &modem_info, 0); + + g_type_interface_add_prerequisite (modem_type, G_TYPE_OBJECT); + + dbus_g_object_type_install_info (modem_type, &dbus_glib_mm_modem_object_info); + } + + return modem_type; +} diff --git a/src/mm-modem.h b/src/mm-modem.h new file mode 100644 index 0000000..e8dd7ea --- /dev/null +++ b/src/mm-modem.h @@ -0,0 +1,219 @@ +/* -*- 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_H +#define MM_MODEM_H + +#include <glib-object.h> + +#include "mm-port.h" + +typedef enum { + MM_MODEM_STATE_UNKNOWN = 0, + MM_MODEM_STATE_DISABLED = 10, + MM_MODEM_STATE_DISABLING = 20, + MM_MODEM_STATE_ENABLING = 30, + MM_MODEM_STATE_ENABLED = 40, + MM_MODEM_STATE_SEARCHING = 50, + MM_MODEM_STATE_REGISTERED = 60, + MM_MODEM_STATE_DISCONNECTING = 70, + MM_MODEM_STATE_CONNECTING = 80, + MM_MODEM_STATE_CONNECTED = 90, + + MM_MODEM_STATE_LAST = MM_MODEM_STATE_CONNECTED +} MMModemState; + +typedef enum { + MM_MODEM_STATE_REASON_NONE = 0 +} MMModemStateReason; + +#define DBUS_PATH_TAG "dbus-path" + +#define MM_TYPE_MODEM (mm_modem_get_type ()) +#define MM_MODEM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_MODEM, MMModem)) +#define MM_IS_MODEM(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_MODEM)) +#define MM_MODEM_GET_INTERFACE(obj) (G_TYPE_INSTANCE_GET_INTERFACE ((obj), MM_TYPE_MODEM, MMModem)) + +#define MM_MODEM_DBUS_INTERFACE "org.freedesktop.ModemManager.Modem" + +#define MM_MODEM_DATA_DEVICE "device" +#define MM_MODEM_MASTER_DEVICE "master-device" +#define MM_MODEM_DRIVER "driver" +#define MM_MODEM_TYPE "type" +#define MM_MODEM_IP_METHOD "ip-method" +#define MM_MODEM_ENABLED "enabled" +#define MM_MODEM_VALID "valid" /* not exported */ +#define MM_MODEM_PLUGIN "plugin" /* not exported */ +#define MM_MODEM_STATE "state" /* not exported */ + +#define MM_MODEM_TYPE_UNKNOWN 0 +#define MM_MODEM_TYPE_GSM 1 +#define MM_MODEM_TYPE_CDMA 2 + +#define MM_MODEM_IP_METHOD_PPP 0 +#define MM_MODEM_IP_METHOD_STATIC 1 +#define MM_MODEM_IP_METHOD_DHCP 2 + +typedef enum { + MM_MODEM_PROP_FIRST = 0x1000, + + MM_MODEM_PROP_DATA_DEVICE = MM_MODEM_PROP_FIRST, + MM_MODEM_PROP_MASTER_DEVICE, + MM_MODEM_PROP_DRIVER, + MM_MODEM_PROP_TYPE, + MM_MODEM_PROP_IP_METHOD, + MM_MODEM_PROP_VALID, /* Not exported */ + MM_MODEM_PROP_PLUGIN, /* Not exported */ + MM_MODEM_PROP_STATE, /* Not exported */ + MM_MODEM_PROP_ENABLED +} MMModemProp; + +typedef struct _MMModem MMModem; + +typedef void (*MMModemFn) (MMModem *modem, + GError *error, + gpointer user_data); + +typedef void (*MMModemUIntFn) (MMModem *modem, + guint32 result, + GError *error, + gpointer user_data); + +typedef void (*MMModemStringFn) (MMModem *modem, + const char *result, + GError *error, + gpointer user_data); + +typedef void (*MMModemIp4Fn) (MMModem *modem, + guint32 address, + GArray *dns, + GError *error, + gpointer user_data); + +typedef void (*MMModemInfoFn) (MMModem *modem, + const char *manufacturer, + const char *model, + const char *version, + GError *error, + gpointer user_data); + +struct _MMModem { + GTypeInterface g_iface; + + /* Methods */ + gboolean (*owns_port) (MMModem *self, + const char *subsys, + const char *name); + + gboolean (*grab_port) (MMModem *self, + const char *subsys, + const char *name, + MMPortType suggested_type, + gpointer user_data, + GError **error); + + void (*release_port) (MMModem *self, + const char *subsys, + const char *name); + + void (*enable) (MMModem *self, + MMModemFn callback, + gpointer user_data); + + void (*disable) (MMModem *self, + MMModemFn callback, + gpointer user_data); + + void (*connect) (MMModem *self, + const char *number, + MMModemFn callback, + gpointer user_data); + + void (*get_ip4_config) (MMModem *self, + MMModemIp4Fn callback, + gpointer user_data); + + void (*disconnect) (MMModem *self, + MMModemFn callback, + gpointer user_data); + + void (*get_info) (MMModem *self, + MMModemInfoFn callback, + gpointer user_data); + + /* Signals */ + void (*state_changed) (MMModem *self, + MMModemState new_state, + MMModemState old_state, + MMModemStateReason reason); +}; + +GType mm_modem_get_type (void); + +gboolean mm_modem_owns_port (MMModem *self, + const char *subsys, + const char *name); + +gboolean mm_modem_grab_port (MMModem *self, + const char *subsys, + const char *name, + MMPortType suggested_type, + gpointer user_data, + GError **error); + +void mm_modem_release_port (MMModem *self, + const char *subsys, + const char *name); + +void mm_modem_enable (MMModem *self, + MMModemFn callback, + gpointer user_data); + +void mm_modem_disable (MMModem *self, + MMModemFn callback, + gpointer user_data); + +void mm_modem_connect (MMModem *self, + const char *number, + MMModemFn callback, + gpointer user_data); + +void mm_modem_get_ip4_config (MMModem *self, + MMModemIp4Fn callback, + gpointer user_data); + +void mm_modem_disconnect (MMModem *self, + MMModemFn callback, + gpointer user_data); + +void mm_modem_get_info (MMModem *self, + MMModemInfoFn callback, + gpointer user_data); + +gboolean mm_modem_get_valid (MMModem *self); + +char *mm_modem_get_device (MMModem *self); + +MMModemState mm_modem_get_state (MMModem *self); + +void mm_modem_set_state (MMModem *self, + MMModemState new_state, + MMModemStateReason reason); + +GError *mm_modem_check_removed (MMModem *self, const GError *error); + +#endif /* MM_MODEM_H */ + diff --git a/src/mm-options.c b/src/mm-options.c new file mode 100644 index 0000000..7bbeefd --- /dev/null +++ b/src/mm-options.c @@ -0,0 +1,55 @@ +/* -*- 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 Novell, Inc. + */ + +#include <stdlib.h> +#include <glib.h> +#include "mm-options.h" + +static gboolean debug = FALSE; + +void +mm_options_parse (int argc, char *argv[]) +{ + GOptionContext *opt_ctx; + GError *error = NULL; + GOptionEntry entries[] = { + { "debug", 0, 0, G_OPTION_ARG_NONE, &debug, "Output to console rather than syslog", NULL }, + { NULL } + }; + + opt_ctx = g_option_context_new (NULL); + g_option_context_set_summary (opt_ctx, "DBus system service to communicate with modems."); + g_option_context_add_main_entries (opt_ctx, entries, NULL); + + if (!g_option_context_parse (opt_ctx, &argc, &argv, &error)) { + g_warning ("%s\n", error->message); + g_error_free (error); + exit (1); + } + + g_option_context_free (opt_ctx); +} + +void +mm_options_set_debug (gboolean enabled) +{ + debug = enabled; +} + +gboolean +mm_options_debug (void) +{ + return debug; +} diff --git a/src/mm-options.h b/src/mm-options.h new file mode 100644 index 0000000..ce33e27 --- /dev/null +++ b/src/mm-options.h @@ -0,0 +1,23 @@ +/* -*- 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 Novell, Inc. + */ + +#ifndef MM_OPTIONS_H +#define MM_OPTIONS_H + +void mm_options_parse (int argc, char *argv[]); +void mm_options_set_debug (gboolean enabled); +gboolean mm_options_debug (void); + +#endif /* MM_OPTIONS_H */ diff --git a/src/mm-plugin-base.c b/src/mm-plugin-base.c new file mode 100644 index 0000000..202eaac --- /dev/null +++ b/src/mm-plugin-base.c @@ -0,0 +1,1126 @@ +/* -*- 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. + */ + +#define _GNU_SOURCE /* for strcasestr */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> + +#include <string.h> + +#define G_UDEV_API_IS_SUBJECT_TO_CHANGE +#include <gudev/gudev.h> + +#include "mm-plugin-base.h" +#include "mm-serial-port.h" +#include "mm-serial-parsers.h" +#include "mm-errors.h" +#include "mm-marshal.h" + +static void plugin_init (MMPlugin *plugin_class); + +G_DEFINE_TYPE_EXTENDED (MMPluginBase, mm_plugin_base, G_TYPE_OBJECT, + 0, G_IMPLEMENT_INTERFACE (MM_TYPE_PLUGIN, plugin_init)) + +#define MM_PLUGIN_BASE_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), MM_TYPE_PLUGIN_BASE, MMPluginBasePrivate)) + +/* A hash table shared between all instances of the plugin base that + * caches the probed capabilities so that only one plugin has to actually + * probe a port. + */ +static GHashTable *cached_caps = NULL; + + +typedef struct { + char *name; + GUdevClient *client; + + GHashTable *modems; + GHashTable *tasks; +} MMPluginBasePrivate; + +enum { + PROP_0, + PROP_NAME, + LAST_PROP +}; + +enum { + PROBE_RESULT, + LAST_SIGNAL +}; + +static guint signals[LAST_SIGNAL] = { 0 }; + + +typedef enum { + PROBE_STATE_GCAP_TRY1 = 0, + PROBE_STATE_GCAP_TRY2, + PROBE_STATE_GCAP_TRY3, + PROBE_STATE_ATI, + PROBE_STATE_CPIN, + PROBE_STATE_CGMM, + PROBE_STATE_LAST +} ProbeState; + +/*****************************************************************************/ + +G_DEFINE_TYPE (MMPluginBaseSupportsTask, mm_plugin_base_supports_task, G_TYPE_OBJECT) + +#define MM_PLUGIN_BASE_SUPPORTS_TASK_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), MM_TYPE_PLUGIN_BASE_SUPPORTS_TASK, MMPluginBaseSupportsTaskPrivate)) + +typedef struct { + MMPluginBase *plugin; + GUdevDevice *port; + GUdevDevice *physdev; + char *driver; + + guint open_id; + guint32 open_tries; + + MMSerialPort *probe_port; + guint32 probed_caps; + ProbeState probe_state; + guint probe_id; + char *probe_resp; + GError *probe_error; + + char *custom_init; + guint32 custom_init_max_tries; + guint32 custom_init_tries; + guint32 custom_init_delay_seconds; + gboolean custom_init_fail_if_timeout; + + MMSupportsPortResultFunc callback; + gpointer callback_data; +} MMPluginBaseSupportsTaskPrivate; + +static MMPluginBaseSupportsTask * +supports_task_new (MMPluginBase *self, + GUdevDevice *port, + GUdevDevice *physdev, + const char *driver, + MMSupportsPortResultFunc callback, + gpointer callback_data) +{ + MMPluginBaseSupportsTask *task; + MMPluginBaseSupportsTaskPrivate *priv; + + g_return_val_if_fail (self != NULL, NULL); + g_return_val_if_fail (MM_IS_PLUGIN_BASE (self), NULL); + g_return_val_if_fail (port != NULL, NULL); + g_return_val_if_fail (physdev != NULL, NULL); + g_return_val_if_fail (driver != NULL, NULL); + g_return_val_if_fail (callback != NULL, NULL); + + task = (MMPluginBaseSupportsTask *) g_object_new (MM_TYPE_PLUGIN_BASE_SUPPORTS_TASK, NULL); + + priv = MM_PLUGIN_BASE_SUPPORTS_TASK_GET_PRIVATE (task); + priv->plugin = self; + priv->port = g_object_ref (port); + priv->physdev = g_object_ref (physdev); + priv->driver = g_strdup (driver); + priv->callback = callback; + priv->callback_data = callback_data; + + return task; +} + +MMPlugin * +mm_plugin_base_supports_task_get_plugin (MMPluginBaseSupportsTask *task) +{ + g_return_val_if_fail (task != NULL, NULL); + g_return_val_if_fail (MM_IS_PLUGIN_BASE_SUPPORTS_TASK (task), NULL); + + return MM_PLUGIN (MM_PLUGIN_BASE_SUPPORTS_TASK_GET_PRIVATE (task)->plugin); +} + +GUdevDevice * +mm_plugin_base_supports_task_get_port (MMPluginBaseSupportsTask *task) +{ + g_return_val_if_fail (task != NULL, NULL); + g_return_val_if_fail (MM_IS_PLUGIN_BASE_SUPPORTS_TASK (task), NULL); + + return MM_PLUGIN_BASE_SUPPORTS_TASK_GET_PRIVATE (task)->port; +} + +GUdevDevice * +mm_plugin_base_supports_task_get_physdev (MMPluginBaseSupportsTask *task) +{ + g_return_val_if_fail (task != NULL, NULL); + g_return_val_if_fail (MM_IS_PLUGIN_BASE_SUPPORTS_TASK (task), NULL); + + return MM_PLUGIN_BASE_SUPPORTS_TASK_GET_PRIVATE (task)->physdev; +} + +const char * +mm_plugin_base_supports_task_get_driver (MMPluginBaseSupportsTask *task) +{ + g_return_val_if_fail (task != NULL, NULL); + g_return_val_if_fail (MM_IS_PLUGIN_BASE_SUPPORTS_TASK (task), NULL); + + return MM_PLUGIN_BASE_SUPPORTS_TASK_GET_PRIVATE (task)->driver; +} + +guint32 +mm_plugin_base_supports_task_get_probed_capabilities (MMPluginBaseSupportsTask *task) +{ + g_return_val_if_fail (task != NULL, 0); + g_return_val_if_fail (MM_IS_PLUGIN_BASE_SUPPORTS_TASK (task), 0); + + return MM_PLUGIN_BASE_SUPPORTS_TASK_GET_PRIVATE (task)->probed_caps; +} + +void +mm_plugin_base_supports_task_complete (MMPluginBaseSupportsTask *task, + guint32 level) +{ + MMPluginBaseSupportsTaskPrivate *priv; + const char *subsys, *name; + + g_return_if_fail (task != NULL); + g_return_if_fail (MM_IS_PLUGIN_BASE_SUPPORTS_TASK (task)); + + priv = MM_PLUGIN_BASE_SUPPORTS_TASK_GET_PRIVATE (task); + g_return_if_fail (priv->callback != NULL); + + subsys = g_udev_device_get_subsystem (priv->port); + name = g_udev_device_get_name (priv->port); + + priv->callback (MM_PLUGIN (priv->plugin), subsys, name, level, priv->callback_data); + + /* Clear out the callback, it shouldn't be called more than once */ + priv->callback = NULL; + priv->callback_data = NULL; +} + +void +mm_plugin_base_supports_task_set_custom_init_command (MMPluginBaseSupportsTask *task, + const char *cmd, + guint32 delay_seconds, + guint32 max_tries, + gboolean fail_if_timeout) +{ + MMPluginBaseSupportsTaskPrivate *priv; + + g_return_if_fail (task != NULL); + g_return_if_fail (MM_IS_PLUGIN_BASE_SUPPORTS_TASK (task)); + + priv = MM_PLUGIN_BASE_SUPPORTS_TASK_GET_PRIVATE (task); + + g_free (priv->custom_init); + priv->custom_init = g_strdup (cmd); + priv->custom_init_max_tries = max_tries; + priv->custom_init_delay_seconds = delay_seconds; + priv->custom_init_fail_if_timeout = fail_if_timeout; +} + +static void +mm_plugin_base_supports_task_init (MMPluginBaseSupportsTask *self) +{ +} + +static void +supports_task_dispose (GObject *object) +{ + MMPluginBaseSupportsTaskPrivate *priv = MM_PLUGIN_BASE_SUPPORTS_TASK_GET_PRIVATE (object); + + if (MM_IS_SERIAL_PORT (priv->port)) + mm_serial_port_flash_cancel (MM_SERIAL_PORT (priv->port)); + + g_object_unref (priv->port); + g_object_unref (priv->physdev); + g_free (priv->driver); + g_free (priv->probe_resp); + g_clear_error (&(priv->probe_error)); + g_free (priv->custom_init); + + if (priv->open_id) + g_source_remove (priv->open_id); + + if (priv->probe_id) + g_source_remove (priv->probe_id); + if (priv->probe_port) + g_object_unref (priv->probe_port); + + G_OBJECT_CLASS (mm_plugin_base_supports_task_parent_class)->dispose (object); +} + +static void +mm_plugin_base_supports_task_class_init (MMPluginBaseSupportsTaskClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + g_type_class_add_private (object_class, sizeof (MMPluginBaseSupportsTaskPrivate)); + + /* Virtual methods */ + object_class->dispose = supports_task_dispose; +} + +/*****************************************************************************/ + +#define MM_PLUGIN_BASE_PORT_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) + +#define CAP_GSM_OR_CDMA (MM_PLUGIN_BASE_PORT_CAP_CDMA | MM_PLUGIN_BASE_PORT_CAP_GSM) + +struct modem_caps { + char *name; + guint32 bits; +}; + +static struct modem_caps modem_caps[] = { + {"+CGSM", MM_PLUGIN_BASE_PORT_CAP_GSM}, + {"+CIS707-A", MM_PLUGIN_BASE_PORT_CAP_IS707_A}, + {"+CIS707A", MM_PLUGIN_BASE_PORT_CAP_IS707_A}, /* Cmotech */ + {"+CIS707", MM_PLUGIN_BASE_PORT_CAP_IS707_A}, + {"CIS707", MM_PLUGIN_BASE_PORT_CAP_IS707_A}, /* Qualcomm Gobi */ + {"+CIS707P", MM_PLUGIN_BASE_PORT_CAP_IS707_P}, + {"CIS-856", MM_PLUGIN_BASE_PORT_CAP_IS856}, + {"+IS-856", MM_PLUGIN_BASE_PORT_CAP_IS856}, /* Cmotech */ + {"CIS-856-A", MM_PLUGIN_BASE_PORT_CAP_IS856_A}, + {"CIS-856A", MM_PLUGIN_BASE_PORT_CAP_IS856_A}, /* Kyocera KPC680 */ + {"+DS", MM_PLUGIN_BASE_PORT_CAP_DS}, + {"+ES", MM_PLUGIN_BASE_PORT_CAP_ES}, + {"+MS", MM_PLUGIN_BASE_PORT_CAP_MS}, + {"+FCLASS", MM_PLUGIN_BASE_PORT_CAP_FCLASS}, + {NULL} +}; + +static guint32 +parse_gcap (const char *buf) +{ + struct modem_caps *cap = modem_caps; + guint32 ret = 0; + + while (cap->name) { + if (strstr (buf, cap->name)) + ret |= cap->bits; + cap++; + } + return ret; +} + +static guint32 +parse_cpin (const char *buf) +{ + if ( strcasestr (buf, "SIM PIN") + || strcasestr (buf, "SIM PUK") + || strcasestr (buf, "PH-SIM PIN") + || strcasestr (buf, "PH-FSIM PIN") + || strcasestr (buf, "PH-FSIM PUK") + || strcasestr (buf, "SIM PIN2") + || strcasestr (buf, "SIM PUK2") + || strcasestr (buf, "PH-NET PIN") + || strcasestr (buf, "PH-NET PUK") + || strcasestr (buf, "PH-NETSUB PIN") + || strcasestr (buf, "PH-NETSUB PUK") + || strcasestr (buf, "PH-SP PIN") + || strcasestr (buf, "PH-SP PUK") + || strcasestr (buf, "PH-CORP PIN") + || strcasestr (buf, "PH-CORP PUK")) + return MM_PLUGIN_BASE_PORT_CAP_GSM; + + return 0; +} + +static guint32 +parse_cgmm (const char *buf) +{ + if (strstr (buf, "GSM900") || strstr (buf, "GSM1800") || + strstr (buf, "GSM1900") || strstr (buf, "GSM850")) + return MM_PLUGIN_BASE_PORT_CAP_GSM; + return 0; +} + +static gboolean +emit_probe_result (gpointer user_data) +{ + MMPluginBaseSupportsTask *task = MM_PLUGIN_BASE_SUPPORTS_TASK (user_data); + MMPluginBaseSupportsTaskPrivate *task_priv = MM_PLUGIN_BASE_SUPPORTS_TASK_GET_PRIVATE (task); + MMPlugin *self = mm_plugin_base_supports_task_get_plugin (task); + + /* Close the serial port */ + g_object_unref (task_priv->probe_port); + task_priv->probe_port = NULL; + + task_priv->probe_id = 0; + g_signal_emit (self, signals[PROBE_RESULT], 0, task, task_priv->probed_caps); + return FALSE; +} + +static void +probe_complete (MMPluginBaseSupportsTask *task) +{ + MMPluginBaseSupportsTaskPrivate *task_priv = MM_PLUGIN_BASE_SUPPORTS_TASK_GET_PRIVATE (task); + + g_hash_table_insert (cached_caps, + g_strdup (mm_port_get_device (MM_PORT (task_priv->probe_port))), + GUINT_TO_POINTER (task_priv->probed_caps)); + + task_priv->probe_id = g_idle_add (emit_probe_result, task); +} + +static void +parse_response (MMSerialPort *port, + GString *response, + GError *error, + gpointer user_data); + +static void +real_handle_probe_response (MMPluginBase *self, + MMPluginBaseSupportsTask *task, + const char *cmd, + const char *response, + const GError *error) +{ + MMPluginBaseSupportsTaskPrivate *task_priv = MM_PLUGIN_BASE_SUPPORTS_TASK_GET_PRIVATE (task); + MMSerialPort *port = task_priv->probe_port; + gboolean ignore_error = FALSE; + + /* Some modems (Huawei E160g) won't respond to +GCAP with no SIM, but + * will respond to ATI. + */ + if (response && strstr (response, "+CME ERROR:")) + ignore_error = TRUE; + + if (error && !ignore_error) { + if (error->code == MM_SERIAL_RESPONSE_TIMEOUT) { + /* Try GCAP again */ + if (task_priv->probe_state < PROBE_STATE_GCAP_TRY3) { + task_priv->probe_state++; + mm_serial_port_queue_command (port, "+GCAP", 3, parse_response, task); + } else { + /* Otherwise, if all the GCAP tries timed out, ignore the port + * as it's probably not an AT-capable port. + */ + probe_complete (task); + } + return; + } + + /* Otherwise proceed to the next command */ + } else if (response) { + /* Parse the response */ + + switch (task_priv->probe_state) { + case PROBE_STATE_GCAP_TRY1: + case PROBE_STATE_GCAP_TRY2: + case PROBE_STATE_GCAP_TRY3: + case PROBE_STATE_ATI: + /* Some modems don't respond to AT+GCAP, but often they put a + * GCAP-style response as a line in the ATI response. + */ + task_priv->probed_caps = parse_gcap (response); + break; + case PROBE_STATE_CPIN: + /* Some devices (ZTE MF628/ONDA MT503HS for example) reply to + * anything but AT+CPIN? with ERROR if the device has a PIN set. + * Since no known CDMA modems support AT+CPIN? we can consider the + * device a GSM device if it returns a non-error response to AT+CPIN?. + */ + task_priv->probed_caps = parse_cpin (response); + break; + case PROBE_STATE_CGMM: + /* Some models (BUSlink SCWi275u) stick stupid stuff in the CGMM + * response but at least it allows us to identify them. + */ + task_priv->probed_caps = parse_cgmm (response); + break; + default: + break; + } + + if (task_priv->probed_caps & CAP_GSM_OR_CDMA) { + probe_complete (task); + return; + } + } + + task_priv->probe_state++; + + /* Try a different command */ + switch (task_priv->probe_state) { + case PROBE_STATE_GCAP_TRY2: + case PROBE_STATE_GCAP_TRY3: + mm_serial_port_queue_command (port, "+GCAP", 3, parse_response, task); + break; + case PROBE_STATE_ATI: + /* After the last GCAP attempt, try ATI */ + mm_serial_port_queue_command (port, "I", 3, parse_response, task); + break; + case PROBE_STATE_CPIN: + /* After the ATI attempt, try CPIN */ + mm_serial_port_queue_command (port, "+CPIN?", 3, parse_response, task); + break; + case PROBE_STATE_CGMM: + /* After the CPIN attempt, try CGMM */ + mm_serial_port_queue_command (port, "+CGMM", 3, parse_response, task); + break; + default: + /* Probably not GSM or CDMA */ + probe_complete (task); + break; + } +} + +static gboolean +handle_probe_response (gpointer user_data) +{ + MMPluginBaseSupportsTask *task = MM_PLUGIN_BASE_SUPPORTS_TASK (user_data); + MMPluginBaseSupportsTaskPrivate *task_priv = MM_PLUGIN_BASE_SUPPORTS_TASK_GET_PRIVATE (task); + MMPluginBase *self = MM_PLUGIN_BASE (mm_plugin_base_supports_task_get_plugin (task)); + const char *cmd = NULL; + + switch (task_priv->probe_state) { + case PROBE_STATE_GCAP_TRY1: + case PROBE_STATE_GCAP_TRY2: + case PROBE_STATE_GCAP_TRY3: + cmd = "+GCAP"; + break; + case PROBE_STATE_ATI: + cmd = "I"; + break; + case PROBE_STATE_CPIN: + cmd = "+CPIN?"; + break; + case PROBE_STATE_CGMM: + default: + cmd = "+CGMM"; + break; + } + + MM_PLUGIN_BASE_GET_CLASS (self)->handle_probe_response (self, + task, + cmd, + task_priv->probe_resp, + task_priv->probe_error); + return FALSE; +} + +static void +parse_response (MMSerialPort *port, + GString *response, + GError *error, + gpointer user_data) +{ + MMPluginBaseSupportsTask *task = MM_PLUGIN_BASE_SUPPORTS_TASK (user_data); + MMPluginBaseSupportsTaskPrivate *task_priv = MM_PLUGIN_BASE_SUPPORTS_TASK_GET_PRIVATE (task); + + if (task_priv->probe_id) + g_source_remove (task_priv->probe_id); + g_free (task_priv->probe_resp); + task_priv->probe_resp = NULL; + g_clear_error (&(task_priv->probe_error)); + + if (response && response->len) + task_priv->probe_resp = g_strdup (response->str); + if (error) + task_priv->probe_error = g_error_copy (error); + + /* Schedule the response handler in an idle, since we can't emit the + * PROBE_RESULT signal from the serial port response handler without + * potentially destroying the serial port in the middle of its response + * handler, which it understandably doesn't like. + */ + task_priv->probe_id = g_idle_add (handle_probe_response, task); +} + +static void flash_done (MMSerialPort *port, GError *error, gpointer user_data); + +static void +custom_init_response (MMSerialPort *port, + GString *response, + GError *error, + gpointer user_data) +{ + MMPluginBaseSupportsTask *task = MM_PLUGIN_BASE_SUPPORTS_TASK (user_data); + MMPluginBaseSupportsTaskPrivate *task_priv = MM_PLUGIN_BASE_SUPPORTS_TASK_GET_PRIVATE (task); + + if (error) { + task_priv->custom_init_tries++; + if (task_priv->custom_init_tries < task_priv->custom_init_max_tries) { + /* Try the custom command again */ + flash_done (port, NULL, user_data); + return; + } else if (task_priv->custom_init_fail_if_timeout) { + /* Fail the probe if the plugin wanted it and the command timed out */ + if (g_error_matches (error, MM_SERIAL_ERROR, MM_SERIAL_RESPONSE_TIMEOUT)) { + probe_complete (task); + return; + } + } + } + + /* Otherwise proceed to probing */ + mm_serial_port_queue_command (port, "+GCAP", 3, parse_response, user_data); +} + +static void +flash_done (MMSerialPort *port, GError *error, gpointer user_data) +{ + MMPluginBaseSupportsTask *task = MM_PLUGIN_BASE_SUPPORTS_TASK (user_data); + MMPluginBaseSupportsTaskPrivate *task_priv = MM_PLUGIN_BASE_SUPPORTS_TASK_GET_PRIVATE (task); + guint32 delay_secs = task_priv->custom_init_delay_seconds; + + /* Send the custom init command if any */ + if (task_priv->custom_init) { + if (!delay_secs) + delay_secs = 3; + mm_serial_port_queue_command (port, + task_priv->custom_init, + delay_secs, + custom_init_response, + user_data); + } else { + /* Otherwise start normal probing */ + custom_init_response (port, NULL, NULL, user_data); + } +} + +static gboolean +try_open (gpointer user_data) +{ + MMPluginBaseSupportsTask *task = MM_PLUGIN_BASE_SUPPORTS_TASK (user_data); + MMPluginBaseSupportsTaskPrivate *task_priv = MM_PLUGIN_BASE_SUPPORTS_TASK_GET_PRIVATE (task); + GError *error = NULL; + + task_priv->open_id = 0; + + if (!mm_serial_port_open (task_priv->probe_port, &error)) { + if (++task_priv->open_tries > 4) { + /* took too long to open the port; give up */ + g_warning ("(%s): failed to open after 4 tries.", + mm_port_get_device (MM_PORT (task_priv->probe_port))); + probe_complete (task); + } else if (g_error_matches (error, + MM_SERIAL_ERROR, + MM_SERIAL_OPEN_FAILED_NO_DEVICE)) { + /* this is nozomi being dumb; try again */ + task_priv->open_id = g_timeout_add_seconds (1, try_open, task); + } else { + /* some other hard error */ + probe_complete (task); + } + g_clear_error (&error); + } else { + /* success, start probing */ + GUdevDevice *port; + + port = mm_plugin_base_supports_task_get_port (task); + g_assert (port); + + g_debug ("(%s): probe requested by plugin '%s'", + g_udev_device_get_name (port), + mm_plugin_get_name (MM_PLUGIN (task_priv->plugin))); + mm_serial_port_flash (task_priv->probe_port, 100, flash_done, task); + } + + return FALSE; +} + +gboolean +mm_plugin_base_probe_port (MMPluginBase *self, + MMPluginBaseSupportsTask *task, + GError **error) +{ + MMPluginBaseSupportsTaskPrivate *task_priv = MM_PLUGIN_BASE_SUPPORTS_TASK_GET_PRIVATE (task); + MMSerialPort *serial; + const char *name; + GUdevDevice *port; + + g_return_val_if_fail (self != NULL, FALSE); + g_return_val_if_fail (MM_IS_PLUGIN_BASE (self), FALSE); + g_return_val_if_fail (task != NULL, FALSE); + + port = mm_plugin_base_supports_task_get_port (task); + g_assert (port); + name = g_udev_device_get_name (port); + g_assert (name); + + serial = mm_serial_port_new (name, MM_PORT_TYPE_PRIMARY); + if (serial == NULL) { + g_set_error_literal (error, MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL, + "Failed to create new serial port."); + return FALSE; + } + + g_object_set (serial, + MM_SERIAL_PORT_SEND_DELAY, (guint64) 100000, + MM_PORT_CARRIER_DETECT, FALSE, + NULL); + + mm_serial_port_set_response_parser (serial, + mm_serial_parser_v1_parse, + mm_serial_parser_v1_new (), + mm_serial_parser_v1_destroy); + + /* Open the port */ + task_priv->probe_port = serial; + task_priv->open_id = g_idle_add (try_open, task); + return TRUE; +} + +gboolean +mm_plugin_base_get_cached_port_capabilities (MMPluginBase *self, + GUdevDevice *port, + guint32 *capabilities) +{ + gpointer tmp = NULL; + gboolean found; + + found = g_hash_table_lookup_extended (cached_caps, g_udev_device_get_name (port), NULL, tmp); + *capabilities = GPOINTER_TO_UINT (tmp); + return found; +} + +/*****************************************************************************/ + +static void +modem_destroyed (gpointer data, GObject *modem) +{ + MMPluginBase *self = MM_PLUGIN_BASE (data); + MMPluginBasePrivate *priv = MM_PLUGIN_BASE_GET_PRIVATE (self); + GHashTableIter iter; + gpointer key, value; + + /* Remove it from the modems info */ + g_hash_table_iter_init (&iter, priv->modems); + while (g_hash_table_iter_next (&iter, &key, &value)) { + if (value == modem) { + g_hash_table_iter_remove (&iter); + break; + } + } + + /* Since we don't track port cached capabilities on a per-modem basis, + * we just have to live with blowing away the cached capabilities whenever + * a modem gets removed. Could do better here by storing a structure + * in the cached capabilities table that includes { caps, modem device } + * or something and then only removing cached capabilities for ports + * that the modem that was just removed owned, but whatever. + */ + g_hash_table_remove_all (cached_caps); +} + +MMModem * +mm_plugin_base_find_modem (MMPluginBase *self, + const char *master_device) +{ + MMPluginBasePrivate *priv; + + g_return_val_if_fail (self != NULL, NULL); + g_return_val_if_fail (MM_IS_PLUGIN_BASE (self), NULL); + g_return_val_if_fail (master_device != NULL, NULL); + g_return_val_if_fail (strlen (master_device) > 0, NULL); + + priv = MM_PLUGIN_BASE_GET_PRIVATE (self); + return g_hash_table_lookup (priv->modems, master_device); +} + +/* From hostap, Copyright (c) 2002-2005, Jouni Malinen <jkmaline@cc.hut.fi> */ + +static int hex2num (char c) +{ + if (c >= '0' && c <= '9') + return c - '0'; + if (c >= 'a' && c <= 'f') + return c - 'a' + 10; + if (c >= 'A' && c <= 'F') + return c - 'A' + 10; + return -1; +} + +static int hex2byte (const char *hex) +{ + int a, b; + a = hex2num(*hex++); + if (a < 0) + return -1; + b = hex2num(*hex++); + if (b < 0) + return -1; + return (a << 4) | b; +} + +/* End from hostap */ + +gboolean +mm_plugin_base_get_device_ids (MMPluginBase *self, + const char *subsys, + const char *name, + guint16 *vendor, + guint16 *product) +{ + MMPluginBasePrivate *priv; + GUdevDevice *device = NULL; + const char *vid, *pid; + gboolean success = FALSE; + + g_return_val_if_fail (self != NULL, FALSE); + g_return_val_if_fail (MM_IS_PLUGIN_BASE (self), FALSE); + g_return_val_if_fail (subsys != NULL, FALSE); + g_return_val_if_fail (name != NULL, FALSE); + if (vendor) + g_return_val_if_fail (*vendor == 0, FALSE); + if (product) + g_return_val_if_fail (*product == 0, FALSE); + + priv = MM_PLUGIN_BASE_GET_PRIVATE (self); + + device = g_udev_client_query_by_subsystem_and_name (priv->client, subsys, name); + if (!device) + goto out; + + vid = g_udev_device_get_property (device, "ID_VENDOR_ID"); + if (!vid || (strlen (vid) != 4)) + goto out; + + if (vendor) { + *vendor = (guint16) (hex2byte (vid + 2) & 0xFF); + *vendor |= (guint16) ((hex2byte (vid) & 0xFF) << 8); + } + + pid = g_udev_device_get_property (device, "ID_MODEL_ID"); + if (!pid || (strlen (pid) != 4)) { + *vendor = 0; + goto out; + } + + if (product) { + *product = (guint16) (hex2byte (pid + 2) & 0xFF); + *product |= (guint16) ((hex2byte (pid) & 0xFF) << 8); + } + + success = TRUE; + +out: + if (device) + g_object_unref (device); + return success; +} + +static char * +get_key (const char *subsys, const char *name) +{ + return g_strdup_printf ("%s%s", subsys, name); +} + +static const char * +get_name (MMPlugin *plugin) +{ + return MM_PLUGIN_BASE_GET_PRIVATE (plugin)->name; +} + +static char * +get_driver_name (GUdevDevice *device) +{ + GUdevDevice *parent = NULL; + const char *driver, *subsys; + char *ret = NULL; + + driver = g_udev_device_get_driver (device); + if (!driver) { + parent = g_udev_device_get_parent (device); + if (parent) + driver = g_udev_device_get_driver (parent); + + /* Check for bluetooth; it's driver is a bunch of levels up so we + * just check for the subsystem of the parent being bluetooth. + */ + if (!driver && parent) { + subsys = g_udev_device_get_subsystem (parent); + if (subsys && !strcmp (subsys, "bluetooth")) + driver = "bluetooth"; + } + } + + if (driver) + ret = g_strdup (driver); + if (parent) + g_object_unref (parent); + + return ret; +} + +static GUdevDevice * +real_find_physical_device (MMPluginBase *plugin, GUdevDevice *child) +{ + GUdevDevice *iter, *old = NULL; + GUdevDevice *physdev = NULL; + const char *subsys, *type; + guint32 i = 0; + gboolean is_usb = FALSE, is_pci = FALSE; + + g_return_val_if_fail (child != NULL, NULL); + + iter = g_object_ref (child); + while (iter && i++ < 8) { + subsys = g_udev_device_get_subsystem (iter); + if (subsys) { + if (is_usb || !strcmp (subsys, "usb")) { + is_usb = TRUE; + type = g_udev_device_get_devtype (iter); + if (type && !strcmp (type, "usb_device")) { + physdev = iter; + break; + } + } else if (is_pci || !strcmp (subsys, "pci")) { + is_pci = TRUE; + physdev = iter; + break; + } + } + + old = iter; + iter = g_udev_device_get_parent (old); + g_object_unref (old); + } + + return physdev; +} + +static MMPluginSupportsResult +supports_port (MMPlugin *plugin, + const char *subsys, + const char *name, + MMSupportsPortResultFunc callback, + gpointer callback_data) +{ + MMPluginBase *self = MM_PLUGIN_BASE (plugin); + MMPluginBasePrivate *priv = MM_PLUGIN_BASE_GET_PRIVATE (self); + GUdevDevice *port = NULL, *physdev = NULL; + char *driver = NULL, *key = NULL; + MMPluginBaseSupportsTask *task; + MMPluginSupportsResult result = MM_PLUGIN_SUPPORTS_PORT_UNSUPPORTED; + MMModem *existing; + const char *master_path; + + key = get_key (subsys, name); + task = g_hash_table_lookup (priv->tasks, key); + if (task) { + g_free (key); + g_return_val_if_fail (task == NULL, MM_PLUGIN_SUPPORTS_PORT_UNSUPPORTED); + } + + port = g_udev_client_query_by_subsystem_and_name (priv->client, subsys, name); + if (!port) + goto out; + + physdev = MM_PLUGIN_BASE_GET_CLASS (self)->find_physical_device (self, port); + if (!physdev) + goto out; + + driver = get_driver_name (port); + if (!driver) + goto out; + + task = supports_task_new (self, port, physdev, driver, callback, callback_data); + g_assert (task); + g_hash_table_insert (priv->tasks, g_strdup (key), g_object_ref (task)); + + /* Help the plugin out a bit by finding an existing modem for this port */ + master_path = g_udev_device_get_sysfs_path (physdev); + existing = g_hash_table_lookup (priv->modems, master_path); + + result = MM_PLUGIN_BASE_GET_CLASS (self)->supports_port (self, existing, task); + if (result != MM_PLUGIN_SUPPORTS_PORT_IN_PROGRESS) { + /* If the plugin doesn't support the port at all, the supports task is + * not needed. + */ + g_hash_table_remove (priv->tasks, key); + } + g_object_unref (task); + +out: + if (physdev) + g_object_unref (physdev); + if (port) + g_object_unref (port); + g_free (key); + g_free (driver); + return result; +} + +static void +cancel_supports_port (MMPlugin *plugin, + const char *subsys, + const char *name) +{ + MMPluginBase *self = MM_PLUGIN_BASE (plugin); + MMPluginBasePrivate *priv = MM_PLUGIN_BASE_GET_PRIVATE (self); + MMPluginBaseSupportsTask *task; + char *key; + + key = get_key (subsys, name); + task = g_hash_table_lookup (priv->tasks, key); + if (task) { + /* Let the plugin cancel any ongoing tasks */ + if (MM_PLUGIN_BASE_GET_CLASS (self)->cancel_task) + MM_PLUGIN_BASE_GET_CLASS (self)->cancel_task (self, task); + g_hash_table_remove (priv->tasks, key); + } + + g_free (key); +} + +static MMModem * +grab_port (MMPlugin *plugin, + const char *subsys, + const char *name, + GError **error) +{ + MMPluginBase *self = MM_PLUGIN_BASE (plugin); + MMPluginBasePrivate *priv = MM_PLUGIN_BASE_GET_PRIVATE (self); + MMPluginBaseSupportsTask *task; + char *key; + MMModem *existing = NULL, *modem = NULL; + const char *master_path; + + key = get_key (subsys, name); + task = g_hash_table_lookup (priv->tasks, key); + if (!task) { + g_free (key); + g_return_val_if_fail (task != NULL, FALSE); + } + + /* Help the plugin out a bit by finding an existing modem for this port */ + master_path = g_udev_device_get_sysfs_path (mm_plugin_base_supports_task_get_physdev (task)); + existing = g_hash_table_lookup (priv->modems, master_path); + + /* Let the modem grab the port */ + modem = MM_PLUGIN_BASE_GET_CLASS (self)->grab_port (self, existing, task, error); + if (modem && !existing) { + g_hash_table_insert (priv->modems, g_strdup (master_path), modem); + g_object_weak_ref (G_OBJECT (modem), modem_destroyed, self); + } + + g_hash_table_remove (priv->tasks, key); + g_free (key); + return modem; +} + +/*****************************************************************************/ + +static void +plugin_init (MMPlugin *plugin_class) +{ + /* interface implementation */ + plugin_class->get_name = get_name; + plugin_class->supports_port = supports_port; + plugin_class->cancel_supports_port = cancel_supports_port; + plugin_class->grab_port = grab_port; +} + +static void +mm_plugin_base_init (MMPluginBase *self) +{ + MMPluginBasePrivate *priv = MM_PLUGIN_BASE_GET_PRIVATE (self); + const char *subsys[] = { "tty", "net", NULL }; + + if (!cached_caps) + cached_caps = g_hash_table_new (g_str_hash, g_str_equal); + + priv->client = g_udev_client_new (subsys); + + priv->modems = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); + priv->tasks = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, + (GDestroyNotify) g_object_unref); +} + +static void +set_property (GObject *object, guint prop_id, + const GValue *value, GParamSpec *pspec) +{ + MMPluginBasePrivate *priv = MM_PLUGIN_BASE_GET_PRIVATE (object); + + switch (prop_id) { + case PROP_NAME: + /* Construct only */ + priv->name = g_value_dup_string (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +get_property (GObject *object, guint prop_id, + GValue *value, GParamSpec *pspec) +{ + MMPluginBasePrivate *priv = MM_PLUGIN_BASE_GET_PRIVATE (object); + + switch (prop_id) { + case PROP_NAME: + g_value_set_string (value, priv->name); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +finalize (GObject *object) +{ + MMPluginBasePrivate *priv = MM_PLUGIN_BASE_GET_PRIVATE (object); + + g_free (priv->name); + + g_object_unref (priv->client); + + g_hash_table_destroy (priv->modems); + g_hash_table_destroy (priv->tasks); + + G_OBJECT_CLASS (mm_plugin_base_parent_class)->finalize (object); +} + +static void +mm_plugin_base_class_init (MMPluginBaseClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + g_type_class_add_private (object_class, sizeof (MMPluginBasePrivate)); + + klass->find_physical_device = real_find_physical_device; + klass->handle_probe_response = real_handle_probe_response; + + /* Virtual methods */ + object_class->get_property = get_property; + object_class->set_property = set_property; + object_class->finalize = finalize; + + g_object_class_install_property + (object_class, PROP_NAME, + g_param_spec_string (MM_PLUGIN_BASE_NAME, + "Name", + "Name", + NULL, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); + + signals[PROBE_RESULT] = + g_signal_new ("probe-result", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (MMPluginBaseClass, probe_result), + NULL, NULL, + mm_marshal_VOID__OBJECT_UINT, + G_TYPE_NONE, 2, G_TYPE_OBJECT, G_TYPE_UINT); +} diff --git a/src/mm-plugin-base.h b/src/mm-plugin-base.h new file mode 100644 index 0000000..49e68d4 --- /dev/null +++ b/src/mm-plugin-base.h @@ -0,0 +1,150 @@ +/* -*- 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) 2009 Red Hat, Inc. + */ + +#ifndef MM_PLUGIN_BASE_H +#define MM_PLUGIN_BASE_H + +#include <glib.h> +#include <glib/gtypes.h> +#include <glib-object.h> + +#define G_UDEV_API_IS_SUBJECT_TO_CHANGE +#include <gudev/gudev.h> + +#include "mm-plugin.h" +#include "mm-modem.h" +#include "mm-port.h" + +#define MM_PLUGIN_BASE_PORT_CAP_GSM 0x0001 /* GSM */ +#define MM_PLUGIN_BASE_PORT_CAP_IS707_A 0x0002 /* CDMA Circuit Switched Data */ +#define MM_PLUGIN_BASE_PORT_CAP_IS707_P 0x0004 /* CDMA Packet Switched Data */ +#define MM_PLUGIN_BASE_PORT_CAP_DS 0x0008 /* Data compression selection (v.42bis) */ +#define MM_PLUGIN_BASE_PORT_CAP_ES 0x0010 /* Error control selection (v.42) */ +#define MM_PLUGIN_BASE_PORT_CAP_FCLASS 0x0020 /* Group III Fax */ +#define MM_PLUGIN_BASE_PORT_CAP_MS 0x0040 /* Modulation selection */ +#define MM_PLUGIN_BASE_PORT_CAP_W 0x0080 /* Wireless commands */ +#define MM_PLUGIN_BASE_PORT_CAP_IS856 0x0100 /* CDMA 3G EVDO rev 0 */ +#define MM_PLUGIN_BASE_PORT_CAP_IS856_A 0x0200 /* CDMA 3G EVDO rev A */ + +#define MM_TYPE_PLUGIN_BASE_SUPPORTS_TASK (mm_plugin_base_supports_task_get_type ()) +#define MM_PLUGIN_BASE_SUPPORTS_TASK(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_PLUGIN_BASE_SUPPORTS_TASK, MMPluginBaseSupportsTask)) +#define MM_PLUGIN_BASE_SUPPORTS_TASK_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_PLUGIN_BASE_SUPPORTS_TASK, MMPluginBaseSupportsTaskClass)) +#define MM_IS_PLUGIN_BASE_SUPPORTS_TASK(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_PLUGIN_BASE_SUPPORTS_TASK)) +#define MM_IS_PLUBIN_BASE_SUPPORTS_TASK_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_PLUGIN_BASE_SUPPORTS_TASK)) +#define MM_PLUGIN_BASE_SUPPORTS_TASK_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_PLUGIN_BASE_SUPPORTS_TASK, MMPluginBaseSupportsTaskClass)) + +typedef struct { + GObject parent; +} MMPluginBaseSupportsTask; + +typedef struct { + GObjectClass parent; +} MMPluginBaseSupportsTaskClass; + +GType mm_plugin_base_supports_task_get_type (void); + +MMPlugin *mm_plugin_base_supports_task_get_plugin (MMPluginBaseSupportsTask *task); + +GUdevDevice *mm_plugin_base_supports_task_get_port (MMPluginBaseSupportsTask *task); + +GUdevDevice *mm_plugin_base_supports_task_get_physdev (MMPluginBaseSupportsTask *task); + +const char *mm_plugin_base_supports_task_get_driver (MMPluginBaseSupportsTask *task); + +guint32 mm_plugin_base_supports_task_get_probed_capabilities (MMPluginBaseSupportsTask *task); + +void mm_plugin_base_supports_task_complete (MMPluginBaseSupportsTask *task, + guint32 level); + +void mm_plugin_base_supports_task_set_custom_init_command (MMPluginBaseSupportsTask *task, + const char *cmd, + guint32 delay_seconds, + guint32 max_tries, + gboolean fail_if_timeout); + +#define MM_TYPE_PLUGIN_BASE (mm_plugin_base_get_type ()) +#define MM_PLUGIN_BASE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_PLUGIN_BASE, MMPluginBase)) +#define MM_PLUGIN_BASE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_PLUGIN_BASE, MMPluginBaseClass)) +#define MM_IS_PLUGIN_BASE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_PLUGIN_BASE)) +#define MM_IS_PLUBIN_BASE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_PLUGIN_BASE)) +#define MM_PLUGIN_BASE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_PLUGIN_BASE, MMPluginBaseClass)) + +#define MM_PLUGIN_BASE_NAME "name" + +typedef struct _MMPluginBase MMPluginBase; +typedef struct _MMPluginBaseClass MMPluginBaseClass; + +struct _MMPluginBase { + GObject parent; +}; + +struct _MMPluginBaseClass { + GObjectClass parent; + + /* Mandatory subclass functions */ + MMPluginSupportsResult (*supports_port) (MMPluginBase *plugin, + MMModem *existing, + MMPluginBaseSupportsTask *task); + + MMModem *(*grab_port) (MMPluginBase *plugin, + MMModem *existing, + MMPluginBaseSupportsTask *task, + GError **error); + + /* Optional subclass functions */ + void (*cancel_task) (MMPluginBase *plugin, + MMPluginBaseSupportsTask *task); + + /* Find a the physical device of a port, ie the USB or PCI or whatever + * "master" device that owns the port. The GUdevDevice object returned + * will be unref-ed by the caller. + */ + GUdevDevice * (*find_physical_device) (MMPluginBase *plugin, + GUdevDevice *port); + + void (*handle_probe_response) (MMPluginBase *plugin, + MMPluginBaseSupportsTask *task, + const char *command, + const char *response, + const GError *error); + + /* Signals */ + void (*probe_result) (MMPluginBase *self, + MMPluginBaseSupportsTask *task, + guint32 capabilities); +}; + +GType mm_plugin_base_get_type (void); + +MMModem *mm_plugin_base_find_modem (MMPluginBase *self, + const char *master_device); + +gboolean mm_plugin_base_get_device_ids (MMPluginBase *self, + const char *subsys, + const char *name, + guint16 *vendor, + guint16 *product); + +gboolean mm_plugin_base_probe_port (MMPluginBase *self, + MMPluginBaseSupportsTask *task, + GError **error); + +/* Returns TRUE if the port was previously probed, FALSE if not */ +gboolean mm_plugin_base_get_cached_port_capabilities (MMPluginBase *self, + GUdevDevice *port, + guint32 *capabilities); + +#endif /* MM_PLUGIN_BASE_H */ + diff --git a/src/mm-plugin.c b/src/mm-plugin.c new file mode 100644 index 0000000..830f2d6 --- /dev/null +++ b/src/mm-plugin.c @@ -0,0 +1,100 @@ +/* -*- 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 Novell, Inc. + * Copyright (C) 2009 Red Hat, Inc. + */ + +#include "mm-plugin.h" + +const char * +mm_plugin_get_name (MMPlugin *plugin) +{ + g_return_val_if_fail (MM_IS_PLUGIN (plugin), NULL); + + return MM_PLUGIN_GET_INTERFACE (plugin)->get_name (plugin); +} + +MMPluginSupportsResult +mm_plugin_supports_port (MMPlugin *plugin, + const char *subsys, + const char *name, + MMSupportsPortResultFunc callback, + gpointer user_data) +{ + g_return_val_if_fail (MM_IS_PLUGIN (plugin), FALSE); + g_return_val_if_fail (subsys != NULL, FALSE); + g_return_val_if_fail (name != NULL, FALSE); + g_return_val_if_fail (callback != NULL, FALSE); + + return MM_PLUGIN_GET_INTERFACE (plugin)->supports_port (plugin, subsys, name, callback, user_data); +} + +void +mm_plugin_cancel_supports_port (MMPlugin *plugin, + const char *subsys, + const char *name) +{ + g_return_if_fail (MM_IS_PLUGIN (plugin)); + g_return_if_fail (subsys != NULL); + g_return_if_fail (name != NULL); + + MM_PLUGIN_GET_INTERFACE (plugin)->cancel_supports_port (plugin, subsys, name); +} + +MMModem * +mm_plugin_grab_port (MMPlugin *plugin, + const char *subsys, + const char *name, + GError **error) +{ + g_return_val_if_fail (MM_IS_PLUGIN (plugin), FALSE); + g_return_val_if_fail (subsys != NULL, FALSE); + g_return_val_if_fail (name != NULL, FALSE); + + return MM_PLUGIN_GET_INTERFACE (plugin)->grab_port (plugin, subsys, name, error); +} + +/*****************************************************************************/ + +static void +mm_plugin_init (gpointer g_iface) +{ +} + +GType +mm_plugin_get_type (void) +{ + static GType plugin_type = 0; + + if (!G_UNLIKELY (plugin_type)) { + const GTypeInfo plugin_info = { + sizeof (MMPlugin), /* class_size */ + mm_plugin_init, /* base_init */ + NULL, /* base_finalize */ + NULL, + NULL, /* class_finalize */ + NULL, /* class_data */ + 0, + 0, /* n_preallocs */ + NULL + }; + + plugin_type = g_type_register_static (G_TYPE_INTERFACE, + "MMPlugin", + &plugin_info, 0); + + g_type_interface_add_prerequisite (plugin_type, G_TYPE_OBJECT); + } + + return plugin_type; +} diff --git a/src/mm-plugin.h b/src/mm-plugin.h new file mode 100644 index 0000000..d1e85b6 --- /dev/null +++ b/src/mm-plugin.h @@ -0,0 +1,120 @@ +/* -*- 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 Novell, Inc. + * Copyright (C) 2009 Red Hat, Inc. + */ + +#ifndef MM_PLUGIN_H +#define MM_PLUGIN_H + +#include <glib-object.h> +#include <mm-modem.h> + +#define MM_PLUGIN_GENERIC_NAME "Generic" + +#define MM_PLUGIN_MAJOR_VERSION 3 +#define MM_PLUGIN_MINOR_VERSION 0 + +#define MM_TYPE_PLUGIN (mm_plugin_get_type ()) +#define MM_PLUGIN(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_PLUGIN, MMPlugin)) +#define MM_IS_PLUGIN(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_PLUGIN)) +#define MM_PLUGIN_GET_INTERFACE(obj) (G_TYPE_INSTANCE_GET_INTERFACE ((obj), MM_TYPE_PLUGIN, MMPlugin)) + +typedef struct _MMPlugin MMPlugin; + +typedef MMPlugin *(*MMPluginCreateFunc) (void); + +/* + * 'level' is a value between 0 and 100 inclusive, where 0 means the plugin has + * no support for the port, and 100 means the plugin has full support for the + * port. + */ +typedef void (*MMSupportsPortResultFunc) (MMPlugin *plugin, + const char *subsys, + const char *name, + guint32 level, + gpointer user_data); + +typedef enum { + MM_PLUGIN_SUPPORTS_PORT_UNSUPPORTED = 0x0, + MM_PLUGIN_SUPPORTS_PORT_IN_PROGRESS, + MM_PLUGIN_SUPPORTS_PORT_DEFER +} MMPluginSupportsResult; + +struct _MMPlugin { + GTypeInterface g_iface; + + /* Methods */ + const char *(*get_name) (MMPlugin *self); + + /* Check whether a plugin supports a particular modem port, and what level + * of support the plugin has for the device. If the plugin can immediately + * determine whether a port is unsupported, it should return + * FALSE. Otherwise, if the plugin needs to perform additional operations + * (ie, probing) to determine the level of support or additional details + * about a port, it should queue that operation (but *not* block on the + * result) and return TRUE to indicate the operation is ongoing. When the + * operation is finished or the level of support is known, the plugin should + * call the provided callback and pass that callback the provided user_data. + */ + MMPluginSupportsResult (*supports_port) (MMPlugin *self, + const char *subsys, + const char *name, + MMSupportsPortResultFunc callback, + gpointer user_data); + + /* Called to cancel an ongoing supports_port() operation or to notify the + * plugin to clean up that operation. For example, if two plugins support + * a particular port, but the first plugin grabs the port, this method will + * be called on the second plugin to allow that plugin to clean up resources + * used while determining it's level of support for the port. + */ + void (*cancel_supports_port) (MMPlugin *self, + const char *subsys, + const char *name); + + /* Will only be called if the plugin returns a value greater than 0 for + * the supports_port() method for this port. The plugin should create and + * return a new modem for the port's device if there is no existing modem + * to handle the port's hardware device, or should add the port to an + * existing modem and return that modem object. If an error is encountered + * while claiming the port, the error information should be returned in the + * error argument, and the plugin should return NULL. + */ + MMModem * (*grab_port) (MMPlugin *self, + const char *subsys, + const char *name, + GError **error); +}; + +GType mm_plugin_get_type (void); + +const char *mm_plugin_get_name (MMPlugin *plugin); + +MMPluginSupportsResult mm_plugin_supports_port (MMPlugin *plugin, + const char *subsys, + const char *name, + MMSupportsPortResultFunc callback, + gpointer user_data); + +void mm_plugin_cancel_supports_port (MMPlugin *plugin, + const char *subsys, + const char *name); + +MMModem *mm_plugin_grab_port (MMPlugin *plugin, + const char *subsys, + const char *name, + GError **error); + +#endif /* MM_PLUGIN_H */ + diff --git a/src/mm-port.c b/src/mm-port.c new file mode 100644 index 0000000..7e8edc4 --- /dev/null +++ b/src/mm-port.c @@ -0,0 +1,278 @@ +/* -*- 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) 2009 Red Hat, Inc. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> + +#include "mm-port.h" + +G_DEFINE_TYPE (MMPort, mm_port, G_TYPE_OBJECT) + +enum { + PROP_0, + PROP_DEVICE, + PROP_SUBSYS, + PROP_TYPE, + PROP_CARRIER_DETECT, + PROP_CONNECTED, + + LAST_PROP +}; + +#define MM_PORT_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), MM_TYPE_PORT, MMPortPrivate)) + +typedef struct { + char *device; + MMPortSubsys subsys; + MMPortType ptype; + gboolean carrier_detect; + gboolean connected; +} MMPortPrivate; + +/*****************************************************************************/ + +static GObject* +constructor (GType type, + guint n_construct_params, + GObjectConstructParam *construct_params) +{ + GObject *object; + MMPortPrivate *priv; + + object = G_OBJECT_CLASS (mm_port_parent_class)->constructor (type, + n_construct_params, + construct_params); + if (!object) + return NULL; + + priv = MM_PORT_GET_PRIVATE (object); + + if (!priv->device) { + g_warning ("MMPort: no device provided"); + g_object_unref (object); + return NULL; + } + + if (priv->subsys == MM_PORT_SUBSYS_UNKNOWN) { + g_warning ("MMPort: invalid port subsystem"); + g_object_unref (object); + return NULL; + } + + if (priv->ptype == MM_PORT_TYPE_UNKNOWN) { + g_warning ("MMPort: invalid port type"); + g_object_unref (object); + return NULL; + } + + return object; +} + + +const char * +mm_port_get_device (MMPort *self) +{ + g_return_val_if_fail (self != NULL, NULL); + g_return_val_if_fail (MM_IS_PORT (self), NULL); + + return MM_PORT_GET_PRIVATE (self)->device; +} + +MMPortSubsys +mm_port_get_subsys (MMPort *self) +{ + g_return_val_if_fail (self != NULL, MM_PORT_SUBSYS_UNKNOWN); + g_return_val_if_fail (MM_IS_PORT (self), MM_PORT_SUBSYS_UNKNOWN); + + return MM_PORT_GET_PRIVATE (self)->subsys; +} + +MMPortType +mm_port_get_port_type (MMPort *self) +{ + g_return_val_if_fail (self != NULL, MM_PORT_TYPE_UNKNOWN); + g_return_val_if_fail (MM_IS_PORT (self), MM_PORT_TYPE_UNKNOWN); + + return MM_PORT_GET_PRIVATE (self)->ptype; +} + +gboolean +mm_port_get_carrier_detect (MMPort *self) +{ + g_return_val_if_fail (self != NULL, MM_PORT_TYPE_UNKNOWN); + g_return_val_if_fail (MM_IS_PORT (self), MM_PORT_TYPE_UNKNOWN); + + return MM_PORT_GET_PRIVATE (self)->carrier_detect; +} + +gboolean +mm_port_get_connected (MMPort *self) +{ + g_return_val_if_fail (self != NULL, FALSE); + g_return_val_if_fail (MM_IS_PORT (self), FALSE); + + return MM_PORT_GET_PRIVATE (self)->connected; +} + +void +mm_port_set_connected (MMPort *self, gboolean connected) +{ + MMPortPrivate *priv; + + g_return_if_fail (self != NULL); + g_return_if_fail (MM_IS_PORT (self)); + + priv = MM_PORT_GET_PRIVATE (self); + if (priv->connected != connected) { + priv->connected = connected; + g_object_notify (G_OBJECT (self), MM_PORT_CONNECTED); + } +} + +/*****************************************************************************/ + +static void +mm_port_init (MMPort *self) +{ +} + +static void +set_property (GObject *object, guint prop_id, + const GValue *value, GParamSpec *pspec) +{ + MMPortPrivate *priv = MM_PORT_GET_PRIVATE (object); + + switch (prop_id) { + case PROP_DEVICE: + /* Construct only */ + priv->device = g_value_dup_string (value); + break; + case PROP_SUBSYS: + /* Construct only */ + priv->subsys = g_value_get_uint (value); + break; + case PROP_TYPE: + /* Construct only */ + priv->ptype = g_value_get_uint (value); + break; + case PROP_CARRIER_DETECT: + priv->carrier_detect = g_value_get_boolean (value); + break; + case PROP_CONNECTED: + priv->connected = g_value_get_boolean (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +get_property (GObject *object, guint prop_id, + GValue *value, GParamSpec *pspec) +{ + MMPortPrivate *priv = MM_PORT_GET_PRIVATE (object); + + switch (prop_id) { + case PROP_DEVICE: + g_value_set_string (value, priv->device); + break; + case PROP_SUBSYS: + g_value_set_uint (value, priv->subsys); + break; + case PROP_TYPE: + g_value_set_uint (value, priv->ptype); + break; + case PROP_CARRIER_DETECT: + g_value_set_boolean (value, priv->carrier_detect); + break; + case PROP_CONNECTED: + g_value_set_boolean (value, priv->connected); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +finalize (GObject *object) +{ + MMPortPrivate *priv = MM_PORT_GET_PRIVATE (object); + + g_free (priv->device); + + G_OBJECT_CLASS (mm_port_parent_class)->finalize (object); +} + +static void +mm_port_class_init (MMPortClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + g_type_class_add_private (object_class, sizeof (MMPortPrivate)); + + /* Virtual methods */ + object_class->constructor = constructor; + object_class->set_property = set_property; + object_class->get_property = get_property; + object_class->finalize = finalize; + + g_object_class_install_property + (object_class, PROP_DEVICE, + g_param_spec_string (MM_PORT_DEVICE, + "Device", + "Device", + NULL, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); + + g_object_class_install_property + (object_class, PROP_SUBSYS, + g_param_spec_uint (MM_PORT_SUBSYS, + "Subsystem", + "Subsystem", + MM_PORT_SUBSYS_UNKNOWN, + MM_PORT_SUBSYS_LAST, + MM_PORT_SUBSYS_UNKNOWN, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); + + g_object_class_install_property + (object_class, PROP_TYPE, + g_param_spec_uint (MM_PORT_TYPE, + "Type", + "Type", + MM_PORT_TYPE_UNKNOWN, + MM_PORT_TYPE_LAST, + MM_PORT_TYPE_UNKNOWN, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); + + g_object_class_install_property + (object_class, PROP_CARRIER_DETECT, + g_param_spec_boolean (MM_PORT_CARRIER_DETECT, + "Carrier Detect", + "Has Carrier Detect", + TRUE, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT)); + + g_object_class_install_property + (object_class, PROP_CONNECTED, + g_param_spec_boolean (MM_PORT_CONNECTED, + "Connected", + "Is connected for data and not usable for control", + FALSE, + G_PARAM_READWRITE)); +} diff --git a/src/mm-port.h b/src/mm-port.h new file mode 100644 index 0000000..b537618 --- /dev/null +++ b/src/mm-port.h @@ -0,0 +1,79 @@ +/* -*- 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) 2009 Red Hat, Inc. + */ + +#ifndef MM_PORT_H +#define MM_PORT_H + +#include <glib.h> +#include <glib/gtypes.h> +#include <glib-object.h> + +typedef enum { + MM_PORT_SUBSYS_UNKNOWN = 0x0, + MM_PORT_SUBSYS_TTY, + MM_PORT_SUBSYS_NET, + + MM_PORT_SUBSYS_LAST = MM_PORT_SUBSYS_NET +} MMPortSubsys; + +typedef enum { + MM_PORT_TYPE_UNKNOWN = 0x0, + MM_PORT_TYPE_PRIMARY, + MM_PORT_TYPE_SECONDARY, + MM_PORT_TYPE_IGNORED, + + MM_PORT_TYPE_LAST = MM_PORT_TYPE_IGNORED +} MMPortType; + +#define MM_TYPE_PORT (mm_port_get_type ()) +#define MM_PORT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_PORT, MMPort)) +#define MM_PORT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_PORT, MMPortClass)) +#define MM_IS_PORT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_PORT)) +#define MM_IS_PORT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_PORT)) +#define MM_PORT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_PORT, MMPortClass)) + +#define MM_PORT_DEVICE "device" +#define MM_PORT_SUBSYS "subsys" +#define MM_PORT_TYPE "type" +#define MM_PORT_CARRIER_DETECT "carrier-detect" +#define MM_PORT_CONNECTED "connected" + +typedef struct _MMPort MMPort; +typedef struct _MMPortClass MMPortClass; + +struct _MMPort { + GObject parent; +}; + +struct _MMPortClass { + GObjectClass parent; +}; + +GType mm_port_get_type (void); + +const char * mm_port_get_device (MMPort *self); + +MMPortSubsys mm_port_get_subsys (MMPort *self); + +MMPortType mm_port_get_port_type (MMPort *self); + +gboolean mm_port_get_carrier_detect (MMPort *self); + +gboolean mm_port_get_connected (MMPort *self); + +void mm_port_set_connected (MMPort *self, gboolean connected); + +#endif /* MM_PORT_H */ + diff --git a/src/mm-properties-changed-signal.c b/src/mm-properties-changed-signal.c new file mode 100644 index 0000000..b7f3672 --- /dev/null +++ b/src/mm-properties-changed-signal.c @@ -0,0 +1,276 @@ +/* -*- 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) 2007 - 2008 Novell, Inc. + * Copyright (C) 2008 - 2009 Red Hat, Inc. + */ + +#include <string.h> +#include <stdio.h> + +#include <dbus/dbus-glib.h> +#include "mm-marshal.h" +#include "mm-properties-changed-signal.h" + +#define DBUS_TYPE_G_MAP_OF_VARIANT (dbus_g_type_get_map ("GHashTable", G_TYPE_STRING, G_TYPE_VALUE)) + +#define PC_SIGNAL_NAME "mm-properties-changed" +#define MM_DBUS_PROPERTY_CHANGED "MM_DBUS_PROPERTY_CHANGED" + +typedef struct { + /* Whitelist of GObject property names for which changes will be emitted + * over the bus. + * + * Mapping of {property-name -> dbus-interface} + */ + GHashTable *registered; + + /* Table of each D-Bus interface of the object for which one or more + * properties have changed, and those properties and their new values. + * Destroyed after the changed signal has been sent. + * + * Mapping of {dbus-interface -> {property-name -> value}} + */ + GHashTable *hash; + + gulong signal_id; + guint idle_id; +} PropertiesChangedInfo; + +static void +destroy_value (gpointer data) +{ + GValue *val = (GValue *) data; + + g_value_unset (val); + g_slice_free (GValue, val); +} + +static PropertiesChangedInfo * +properties_changed_info_new (void) +{ + PropertiesChangedInfo *info; + + info = g_slice_new0 (PropertiesChangedInfo); + + info->registered = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free); + info->hash = g_hash_table_new_full (g_str_hash, g_str_equal, + (GDestroyNotify) g_free, + (GDestroyNotify) g_hash_table_destroy); + return info; +} + +static void +properties_changed_info_destroy (gpointer data) +{ + PropertiesChangedInfo *info = (PropertiesChangedInfo *) data; + + if (info->idle_id) + g_source_remove (info->idle_id); + + g_hash_table_destroy (info->hash); + g_hash_table_destroy (info->registered); + g_slice_free (PropertiesChangedInfo, info); +} + +#ifdef DEBUG +static void +add_to_string (gpointer key, gpointer value, gpointer user_data) +{ + char *buf = (char *) user_data; + GValue str_val = { 0, }; + + g_value_init (&str_val, G_TYPE_STRING); + if (!g_value_transform ((GValue *) value, &str_val)) { + if (G_VALUE_HOLDS_OBJECT (value)) { + GObject *obj = g_value_get_object (value); + + if (g_value_get_object (value)) { + sprintf (buf + strlen (buf), "{%s: %p (%s)}, ", + (const char *) key, obj, G_OBJECT_TYPE_NAME (obj)); + } else { + sprintf (buf + strlen (buf), "{%s: %p}, ", (const char *) key, obj); + } + } else + sprintf (buf + strlen (buf), "{%s: <transform error>}, ", (const char *) key); + } else { + sprintf (buf + strlen (buf), "{%s: %s}, ", (const char *) key, g_value_get_string (&str_val)); + } + g_value_unset (&str_val); +} +#endif + +static gboolean +properties_changed (gpointer data) +{ + GObject *object = G_OBJECT (data); + PropertiesChangedInfo *info; + GHashTableIter iter; + gpointer key, value; + + info = (PropertiesChangedInfo *) g_object_get_data (object, MM_DBUS_PROPERTY_CHANGED); + g_assert (info); + + g_hash_table_iter_init (&iter, info->hash); + while (g_hash_table_iter_next (&iter, &key, &value)) { + const char *interface = (const char *) key; + GHashTable *props = (GHashTable *) value; + +#ifdef DEBUG + { + char buf[2048] = { 0, }; + g_hash_table_foreach (props, add_to_string, &buf); + g_message ("%s: %s -> (%s) %s", __func__, + G_OBJECT_TYPE_NAME (object), + interface, + buf); + } +#endif + + /* Send the PropertiesChanged signal */ + g_signal_emit (object, info->signal_id, 0, interface, props); + } + g_hash_table_remove_all (info->hash); + + return FALSE; +} + +static void +idle_id_reset (gpointer data) +{ + GObject *object = G_OBJECT (data); + PropertiesChangedInfo *info = (PropertiesChangedInfo *) g_object_get_data (object, MM_DBUS_PROPERTY_CHANGED); + + /* info is unset when the object is being destroyed */ + if (info) + info->idle_id = 0; +} + +static char* +uscore_to_wincaps (const char *uscore) +{ + const char *p; + GString *str; + gboolean last_was_uscore; + + last_was_uscore = TRUE; + + str = g_string_new (NULL); + p = uscore; + while (p && *p) { + if (*p == '-' || *p == '_') + last_was_uscore = TRUE; + else { + if (last_was_uscore) { + g_string_append_c (str, g_ascii_toupper (*p)); + last_was_uscore = FALSE; + } else + g_string_append_c (str, *p); + } + ++p; + } + + return g_string_free (str, FALSE); +} + +static PropertiesChangedInfo * +get_properties_changed_info (GObject *object) +{ + PropertiesChangedInfo *info = NULL; + + info = (PropertiesChangedInfo *) g_object_get_data (object, MM_DBUS_PROPERTY_CHANGED); + if (!info) { + info = properties_changed_info_new (); + g_object_set_data_full (object, MM_DBUS_PROPERTY_CHANGED, info, properties_changed_info_destroy); + info->signal_id = g_signal_lookup (PC_SIGNAL_NAME, G_OBJECT_TYPE (object)); + g_assert (info->signal_id); + } + + g_assert (info); + return info; +} + +static void +notify (GObject *object, GParamSpec *pspec) +{ + GHashTable *interfaces; + PropertiesChangedInfo *info; + const char *interface; + GValue *value; + + info = get_properties_changed_info (object); + + interface = g_hash_table_lookup (info->registered, pspec->name); + if (!interface) + return; + + /* Check if there are other changed properties for this interface already, + * otherwise create a new hash table for all changed properties for this + * D-Bus interface. + */ + interfaces = g_hash_table_lookup (info->hash, interface); + if (!interfaces) { + interfaces = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, destroy_value); + g_hash_table_insert (info->hash, g_strdup (interface), interfaces); + } + + /* Now put the changed property value into the hash table of changed values + * for its D-Bus interface. + */ + value = g_slice_new0 (GValue); + g_value_init (value, pspec->value_type); + g_object_get_property (object, pspec->name, value); + g_hash_table_insert (interfaces, uscore_to_wincaps (pspec->name), value); + + if (!info->idle_id) + info->idle_id = g_idle_add_full (G_PRIORITY_DEFAULT_IDLE, properties_changed, object, idle_id_reset); +} + +void +mm_properties_changed_signal_register_property (GObject *object, + const char *property, + const char *interface) +{ + PropertiesChangedInfo *info; + const char *tmp; + + /* All exported properties need to be registered explicitly for now since + * dbus-glib doesn't expose any method to find out the properties registered + * in the XML. + */ + + info = get_properties_changed_info (object); + tmp = g_hash_table_lookup (info->registered, property); + if (tmp) { + g_warning ("%s: property '%s' already registerd on interface '%s'", + __func__, property, tmp); + } else + g_hash_table_insert (info->registered, g_strdup (property), g_strdup (interface)); +} + +guint +mm_properties_changed_signal_new (GObjectClass *object_class) +{ + guint id; + + object_class->notify = notify; + + id = g_signal_new (PC_SIGNAL_NAME, + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_FIRST, + 0, NULL, NULL, + mm_marshal_VOID__STRING_BOXED, + G_TYPE_NONE, 2, G_TYPE_STRING, DBUS_TYPE_G_MAP_OF_VARIANT); + + return id; +} + diff --git a/src/mm-properties-changed-signal.h b/src/mm-properties-changed-signal.h new file mode 100644 index 0000000..60e71b9 --- /dev/null +++ b/src/mm-properties-changed-signal.h @@ -0,0 +1,28 @@ +/* -*- 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) 2007 - 2008 Novell, Inc. + * Copyright (C) 2008 - 2009 Red Hat, Inc. + */ + +#ifndef _MM_PROPERTIES_CHANGED_SIGNAL_H_ +#define _MM_PROPERTIES_CHANGED_SIGNAL_H_ + +#include <glib-object.h> + +guint mm_properties_changed_signal_new (GObjectClass *object_class); + +void mm_properties_changed_signal_register_property (GObject *object, + const char *property, + const char *interface); + +#endif /* _MM_PROPERTIES_CHANGED_SIGNAL_H_ */ diff --git a/src/mm-serial-parsers.c b/src/mm-serial-parsers.c new file mode 100644 index 0000000..58985d9 --- /dev/null +++ b/src/mm-serial-parsers.c @@ -0,0 +1,372 @@ +/* -*- 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. + */ + +#include <string.h> +#include <stdlib.h> + +#include "mm-serial-parsers.h" +#include "mm-errors.h" + +/* Clean up the response by removing control characters like <CR><LF> etc */ +static void +response_clean (GString *response) +{ + char *s; + + /* Ends with one or more '<CR><LF>' */ + s = response->str + response->len - 1; + while ((s > response->str) && (*s == '\n') && (*(s - 1) == '\r')) { + g_string_truncate (response, response->len - 2); + s -= 2; + } + + /* Starts with one or more '<CR><LF>' */ + s = response->str; + while ((response->len >= 2) && (*s == '\r') && (*(s + 1) == '\n')) { + g_string_erase (response, 0, 2); + s = response->str; + } +} + + +static gboolean +remove_eval_cb (const GMatchInfo *match_info, + GString *result, + gpointer user_data) +{ + int *result_len = (int *) user_data; + int start; + int end; + + if (g_match_info_fetch_pos (match_info, 0, &start, &end)) + *result_len -= (end - start); + + return TRUE; +} + +static void +remove_matches (GRegex *r, GString *string) +{ + char *str; + int result_len = string->len; + + str = g_regex_replace_eval (r, string->str, string->len, 0, 0, + remove_eval_cb, &result_len, NULL); + + g_string_truncate (string, 0); + g_string_append_len (string, str, result_len); + g_free (str); +} + +typedef struct { + GRegex *generic_response; + GRegex *detailed_error; +} MMSerialParserV0; + +gpointer +mm_serial_parser_v0_new (void) +{ + MMSerialParserV0 *parser; + GRegexCompileFlags flags = G_REGEX_DOLLAR_ENDONLY | G_REGEX_RAW | G_REGEX_OPTIMIZE; + + parser = g_slice_new (MMSerialParserV0); + + parser->generic_response = g_regex_new ("(\\d)\\0?\\r$", flags, 0, NULL); + parser->detailed_error = g_regex_new ("\\+CME ERROR: (\\d+)\\r\\n$", flags, 0, NULL); + + return parser; +} + +gboolean +mm_serial_parser_v0_parse (gpointer data, + GString *response, + GError **error) +{ + MMSerialParserV0 *parser = (MMSerialParserV0 *) data; + GMatchInfo *match_info; + char *str; + GError *local_error = NULL; + int code; + gboolean found; + + g_return_val_if_fail (parser != NULL, FALSE); + g_return_val_if_fail (response != NULL, FALSE); + + if (G_UNLIKELY (!response->len || !strlen (response->str))) + return FALSE; + + found = g_regex_match_full (parser->generic_response, response->str, response->len, 0, 0, &match_info, NULL); + if (found) { + str = g_match_info_fetch (match_info, 1); + if (str) { + code = atoi (str); + g_free (str); + } else + code = MM_MOBILE_ERROR_UNKNOWN; + + g_match_info_free (match_info); + + switch (code) { + case 0: /* OK */ + break; + case 1: /* CONNECT */ + break; + case 3: /* NO CARRIER */ + local_error = mm_modem_connect_error_for_code (MM_MODEM_CONNECT_ERROR_NO_CARRIER); + break; + case 4: /* ERROR */ + local_error = mm_mobile_error_for_code (MM_MOBILE_ERROR_UNKNOWN); + break; + case 6: /* NO DIALTONE */ + local_error = mm_modem_connect_error_for_code (MM_MODEM_CONNECT_ERROR_NO_DIALTONE); + break; + case 7: /* BUSY */ + local_error = mm_modem_connect_error_for_code (MM_MODEM_CONNECT_ERROR_BUSY); + break; + case 8: /* NO ANSWER */ + local_error = mm_modem_connect_error_for_code (MM_MODEM_CONNECT_ERROR_NO_ANSWER); + break; + default: + local_error = mm_mobile_error_for_code (MM_MOBILE_ERROR_UNKNOWN); + break; + } + + remove_matches (parser->generic_response, response); + } + + if (!found) { + found = g_regex_match_full (parser->detailed_error, response->str, response->len, 0, 0, &match_info, NULL); + + if (found) { + str = g_match_info_fetch (match_info, 1); + if (str) { + code = atoi (str); + g_free (str); + } else + code = MM_MOBILE_ERROR_UNKNOWN; + + g_match_info_free (match_info); + local_error = mm_mobile_error_for_code (code); + } + } + + if (found) + response_clean (response); + + if (local_error) { + g_debug ("Got failure code %d: %s", local_error->code, local_error->message); + g_propagate_error (error, local_error); + } + + return found; +} + +void +mm_serial_parser_v0_destroy (gpointer data) +{ + MMSerialParserV0 *parser = (MMSerialParserV0 *) data; + + g_return_if_fail (parser != NULL); + + g_regex_unref (parser->generic_response); + g_regex_unref (parser->detailed_error); + + g_slice_free (MMSerialParserV0, data); +} + +typedef struct { + GRegex *regex_ok; + GRegex *regex_connect; + GRegex *regex_detailed_error; + GRegex *regex_unknown_error; + GRegex *regex_connect_failed; +} MMSerialParserV1; + +gpointer +mm_serial_parser_v1_new (void) +{ + MMSerialParserV1 *parser; + GRegexCompileFlags flags = G_REGEX_DOLLAR_ENDONLY | G_REGEX_RAW | G_REGEX_OPTIMIZE; + + parser = g_slice_new (MMSerialParserV1); + + parser->regex_ok = g_regex_new ("\\r\\nOK(\\r\\n)+$", flags, 0, NULL); + parser->regex_connect = g_regex_new ("\\r\\nCONNECT.*\\r\\n", flags, 0, NULL); + parser->regex_detailed_error = g_regex_new ("\\r\\n\\+CME ERROR: (\\d+)\\r\\n$", flags, 0, NULL); + parser->regex_unknown_error = g_regex_new ("\\r\\n(ERROR)|(COMMAND NOT SUPPORT)\\r\\n$", flags, 0, NULL); + parser->regex_connect_failed = g_regex_new ("\\r\\n(NO CARRIER)|(BUSY)|(NO ANSWER)|(NO DIALTONE)\\r\\n$", flags, 0, NULL); + + return parser; +} + +gboolean +mm_serial_parser_v1_parse (gpointer data, + GString *response, + GError **error) +{ + MMSerialParserV1 *parser = (MMSerialParserV1 *) data; + GMatchInfo *match_info; + GError *local_error; + int code; + gboolean found = FALSE; + + g_return_val_if_fail (parser != NULL, FALSE); + g_return_val_if_fail (response != NULL, FALSE); + + if (G_UNLIKELY (!response->len || !strlen (response->str))) + return FALSE; + + /* First, check for successful responses */ + + found = g_regex_match_full (parser->regex_ok, response->str, response->len, 0, 0, NULL, NULL); + if (found) + remove_matches (parser->regex_ok, response); + else + found = g_regex_match_full (parser->regex_connect, response->str, response->len, 0, 0, NULL, NULL); + + if (found) { + response_clean (response); + return TRUE; + } + + /* Now failures */ + code = MM_MOBILE_ERROR_UNKNOWN; + local_error = NULL; + + found = g_regex_match_full (parser->regex_detailed_error, + response->str, response->len, + 0, 0, &match_info, NULL); + + if (found) { + char *str; + + str = g_match_info_fetch (match_info, 1); + if (str) { + code = atoi (str); + g_free (str); + } + g_match_info_free (match_info); + } else + found = g_regex_match_full (parser->regex_unknown_error, response->str, response->len, 0, 0, NULL, NULL); + + if (found) + local_error = mm_mobile_error_for_code (code); + else { + found = g_regex_match_full (parser->regex_connect_failed, + response->str, response->len, + 0, 0, &match_info, NULL); + if (found) { + char *str; + + str = g_match_info_fetch (match_info, 1); + if (str) { + if (!strcmp (str, "NO CARRIER")) + code = MM_MODEM_CONNECT_ERROR_NO_CARRIER; + else if (!strcmp (str, "BUSY")) + code = MM_MODEM_CONNECT_ERROR_BUSY; + else if (!strcmp (str, "NO ANSWER")) + code = MM_MODEM_CONNECT_ERROR_NO_ANSWER; + else if (!strcmp (str, "NO DIALTONE")) + code = MM_MODEM_CONNECT_ERROR_NO_DIALTONE; + else + /* uhm... make something up (yes, ok, lie!). */ + code = MM_MODEM_CONNECT_ERROR_NO_CARRIER; + + g_free (str); + } + g_match_info_free (match_info); + + local_error = mm_modem_connect_error_for_code (code); + } + } + + if (found) + response_clean (response); + + if (local_error) { + g_debug ("Got failure code %d: %s", local_error->code, local_error->message); + g_propagate_error (error, local_error); + } + + return found; +} + +void +mm_serial_parser_v1_destroy (gpointer data) +{ + MMSerialParserV1 *parser = (MMSerialParserV1 *) data; + + g_return_if_fail (parser != NULL); + + g_regex_unref (parser->regex_ok); + g_regex_unref (parser->regex_connect); + g_regex_unref (parser->regex_detailed_error); + g_regex_unref (parser->regex_unknown_error); + g_regex_unref (parser->regex_connect_failed); + + g_slice_free (MMSerialParserV1, data); +} + +typedef struct { + gpointer v1; + GRegex *regex_echo; +} MMSerialParserV1E1; + +gpointer +mm_serial_parser_v1_e1_new (void) +{ + MMSerialParserV1E1 *parser; + GRegexCompileFlags flags = G_REGEX_DOLLAR_ENDONLY | G_REGEX_RAW | G_REGEX_OPTIMIZE; + + parser = g_slice_new (MMSerialParserV1E1); + parser->v1 = mm_serial_parser_v1_new (); + + /* Does not start with '<CR><LF>' and ends with '<CR>'. */ + parser->regex_echo = g_regex_new ("^(?!\\r\\n).+\\r", flags, 0, NULL); + + return parser; +} + +gboolean +mm_serial_parser_v1_e1_parse (gpointer data, + GString *response, + GError **error) +{ + MMSerialParserV1E1 *parser = (MMSerialParserV1E1 *) data; + GMatchInfo *match_info = NULL; + + /* Remove the command echo */ + if (g_regex_match_full (parser->regex_echo, response->str, response->len, 0, 0, &match_info, NULL)) { + gchar *match = g_match_info_fetch (match_info, 0); + + g_string_erase (response, 0, strlen (match)); + g_free (match); + g_match_info_free (match_info); + } + + return mm_serial_parser_v1_parse (parser->v1, response, error); +} + +void +mm_serial_parser_v1_e1_destroy (gpointer data) +{ + MMSerialParserV1E1 *parser = (MMSerialParserV1E1 *) data; + + g_regex_unref (parser->regex_echo); + mm_serial_parser_v1_destroy (parser->v1); + + g_slice_free (MMSerialParserV1E1, data); +} diff --git a/src/mm-serial-parsers.h b/src/mm-serial-parsers.h new file mode 100644 index 0000000..3e1fb9f --- /dev/null +++ b/src/mm-serial-parsers.h @@ -0,0 +1,44 @@ +/* -*- 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. + */ + +#ifndef MM_SERIAL_PARSERS_H +#define MM_SERIAL_PARSERS_H + +#include <glib.h> + +gpointer mm_serial_parser_v0_new (void); +gboolean mm_serial_parser_v0_parse (gpointer parser, + GString *response, + GError **error); + +void mm_serial_parser_v0_destroy (gpointer parser); + + +gpointer mm_serial_parser_v1_new (void); +gboolean mm_serial_parser_v1_parse (gpointer parser, + GString *response, + GError **error); + +void mm_serial_parser_v1_destroy (gpointer parser); + + +gpointer mm_serial_parser_v1_e1_new (void); +gboolean mm_serial_parser_v1_e1_parse (gpointer parser, + GString *response, + GError **error); + +void mm_serial_parser_v1_e1_destroy (gpointer parser); + +#endif /* MM_SERIAL_PARSERS_H */ diff --git a/src/mm-serial-port.c b/src/mm-serial-port.c new file mode 100644 index 0000000..2600ae5 --- /dev/null +++ b/src/mm-serial-port.c @@ -0,0 +1,1281 @@ +/* -*- 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. + */ + +#define _GNU_SOURCE /* for strcasestr() */ + +#include <stdio.h> +#include <stdlib.h> +#include <termio.h> +#include <unistd.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <errno.h> +#include <sys/ioctl.h> +#include <string.h> + +#include "mm-serial-port.h" +#include "mm-errors.h" +#include "mm-options.h" + +static gboolean mm_serial_port_queue_process (gpointer data); + +G_DEFINE_TYPE (MMSerialPort, mm_serial_port, MM_TYPE_PORT) + +enum { + PROP_0, + PROP_BAUD, + PROP_BITS, + PROP_PARITY, + PROP_STOPBITS, + PROP_SEND_DELAY, + + LAST_PROP +}; + +#define SERIAL_BUF_SIZE 2048 + +#define MM_SERIAL_PORT_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), MM_TYPE_SERIAL_PORT, MMSerialPortPrivate)) + +typedef struct { + int fd; + GHashTable *reply_cache; + GIOChannel *channel; + GQueue *queue; + GString *command; + GString *response; + + gboolean connected; + + /* Response parser data */ + MMSerialResponseParserFn response_parser_fn; + gpointer response_parser_user_data; + GDestroyNotify response_parser_notify; + GSList *unsolicited_msg_handlers; + + struct termios old_t; + + guint baud; + guint bits; + char parity; + guint stopbits; + guint64 send_delay; + + guint queue_schedule; + guint watch_id; + guint timeout_id; + + guint flash_id; + guint connected_id; +} MMSerialPortPrivate; + +#if 0 +static const char * +baud_to_string (int baud) +{ + const char *speed = NULL; + + switch (baud) { + case B0: + speed = "0"; + break; + case B50: + speed = "50"; + break; + case B75: + speed = "75"; + break; + case B110: + speed = "110"; + break; + case B150: + speed = "150"; + break; + case B300: + speed = "300"; + break; + case B600: + speed = "600"; + break; + case B1200: + speed = "1200"; + break; + case B2400: + speed = "2400"; + break; + case B4800: + speed = "4800"; + break; + case B9600: + speed = "9600"; + break; + case B19200: + speed = "19200"; + break; + case B38400: + speed = "38400"; + break; + case B57600: + speed = "57600"; + break; + case B115200: + speed = "115200"; + break; + case B460800: + speed = "460800"; + break; + default: + break; + } + + return speed; +} + +void +mm_serial_port_print_config (MMSerialPort *port, const char *detail) +{ + MMSerialPortPrivate *priv = MM_SERIAL_PORT_GET_PRIVATE (port); + struct termio stbuf; + int err; + + err = ioctl (priv->fd, TCGETA, &stbuf); + if (err) { + g_warning ("*** %s (%s): (%s) TCGETA error %d", + __func__, detail, mm_port_get_device (MM_PORT (port)), errno); + return; + } + + g_message ("*** %s (%s): (%s) baud rate: %d (%s)", + __func__, detail, mm_port_get_device (MM_PORT (port)), + stbuf.c_cflag & CBAUD, + baud_to_string (stbuf.c_cflag & CBAUD)); +} +#endif + +typedef struct { + GRegex *regex; + MMSerialUnsolicitedMsgFn callback; + gpointer user_data; + GDestroyNotify notify; +} MMUnsolicitedMsgHandler; + +static void +mm_serial_port_set_cached_reply (MMSerialPort *self, + const char *command, + const char *reply) +{ + if (reply) + g_hash_table_insert (MM_SERIAL_PORT_GET_PRIVATE (self)->reply_cache, + g_strdup (command), + g_strdup (reply)); + else + g_hash_table_remove (MM_SERIAL_PORT_GET_PRIVATE (self)->reply_cache, command); +} + +static const char * +mm_serial_port_get_cached_reply (MMSerialPort *self, + const char *command) +{ + return (char *) g_hash_table_lookup (MM_SERIAL_PORT_GET_PRIVATE (self)->reply_cache, command); +} + +static int +parse_baudrate (guint i) +{ + int speed; + + switch (i) { + case 0: + speed = B0; + break; + case 50: + speed = B50; + break; + case 75: + speed = B75; + break; + case 110: + speed = B110; + break; + case 150: + speed = B150; + break; + case 300: + speed = B300; + break; + case 600: + speed = B600; + break; + case 1200: + speed = B1200; + break; + case 2400: + speed = B2400; + break; + case 4800: + speed = B4800; + break; + case 9600: + speed = B9600; + break; + case 19200: + speed = B19200; + break; + case 38400: + speed = B38400; + break; + case 57600: + speed = B57600; + break; + case 115200: + speed = B115200; + break; + case 460800: + speed = B460800; + break; + default: + g_warning ("Invalid baudrate '%d'", i); + speed = B9600; + } + + return speed; +} + +static int +parse_bits (guint i) +{ + int bits; + + switch (i) { + case 5: + bits = CS5; + break; + case 6: + bits = CS6; + break; + case 7: + bits = CS7; + break; + case 8: + bits = CS8; + break; + default: + g_warning ("Invalid bits (%d). Valid values are 5, 6, 7, 8.", i); + bits = CS8; + } + + return bits; +} + +static int +parse_parity (char c) +{ + int parity; + + switch (c) { + case 'n': + case 'N': + parity = 0; + break; + case 'e': + case 'E': + parity = PARENB; + break; + case 'o': + case 'O': + parity = PARENB | PARODD; + break; + default: + g_warning ("Invalid parity (%c). Valid values are n, e, o", c); + parity = 0; + } + + return parity; +} + +static int +parse_stopbits (guint i) +{ + int stopbits; + + switch (i) { + case 1: + stopbits = 0; + break; + case 2: + stopbits = CSTOPB; + break; + default: + g_warning ("Invalid stop bits (%d). Valid values are 1 and 2)", i); + stopbits = 0; + } + + return stopbits; +} + +static gboolean +config_fd (MMSerialPort *self, GError **error) +{ + MMSerialPortPrivate *priv = MM_SERIAL_PORT_GET_PRIVATE (self); + struct termio stbuf; + int speed; + int bits; + int parity; + int stopbits; + + speed = parse_baudrate (priv->baud); + bits = parse_bits (priv->bits); + parity = parse_parity (priv->parity); + stopbits = parse_stopbits (priv->stopbits); + + memset (&stbuf, 0, sizeof (struct termio)); + if (ioctl (priv->fd, TCGETA, &stbuf) != 0) { + g_warning ("%s (%s): TCGETA error: %d", + __func__, + mm_port_get_device (MM_PORT (self)), + errno); + } + + stbuf.c_iflag &= ~(IGNCR | ICRNL | IUCLC | INPCK | IXON | IXANY | IGNPAR ); + stbuf.c_oflag &= ~(OPOST | OLCUC | OCRNL | ONLCR | ONLRET); + stbuf.c_lflag &= ~(ICANON | XCASE | ECHO | ECHOE | ECHONL); + stbuf.c_lflag &= ~(ECHO | ECHOE); + stbuf.c_cc[VMIN] = 1; + stbuf.c_cc[VTIME] = 0; + stbuf.c_cc[VEOF] = 1; + + stbuf.c_cflag &= ~(CBAUD | CSIZE | CSTOPB | CLOCAL | PARENB); + stbuf.c_cflag |= (speed | bits | CREAD | 0 | parity | stopbits); + + if (ioctl (priv->fd, TCSETA, &stbuf) < 0) { + g_set_error (error, + MM_MODEM_ERROR, + MM_MODEM_ERROR_GENERAL, + "%s: failed to set serial port attributes; errno %d", + __func__, errno); + return FALSE; + } + + return TRUE; +} + +static void +serial_debug (MMSerialPort *self, const char *prefix, const char *buf, int len) +{ + static GString *debug = NULL; + const char *s; + + if (!mm_options_debug ()) + return; + + if (len < 0) + len = strlen (buf); + + if (!debug) + debug = g_string_sized_new (256); + + g_string_append (debug, prefix); + g_string_append (debug, " '"); + + s = buf; + while (len--) { + if (g_ascii_isprint (*s)) + g_string_append_c (debug, *s); + else if (*s == '\r') + g_string_append (debug, "<CR>"); + else if (*s == '\n') + g_string_append (debug, "<LF>"); + else + g_string_append_printf (debug, "\\%d", *s); + + s++; + } + + g_string_append_c (debug, '\''); + g_debug ("(%s): %s", mm_port_get_device (MM_PORT (self)), debug->str); + g_string_truncate (debug, 0); +} + +static gboolean +mm_serial_port_send_command (MMSerialPort *self, + const char *command, + GError **error) +{ + MMSerialPortPrivate *priv = MM_SERIAL_PORT_GET_PRIVATE (self); + const char *s; + int status; + int eagain_count = 1000; + + if (priv->fd < 0) { + g_set_error (error, MM_SERIAL_ERROR, MM_SERIAL_SEND_FAILED, + "%s", "Sending command failed: device is not enabled"); + return FALSE; + } + + if (mm_port_get_connected (MM_PORT (self))) { + g_set_error (error, MM_SERIAL_ERROR, MM_SERIAL_SEND_FAILED, + "%s", "Sending command failed: device is connected"); + return FALSE; + } + + g_string_truncate (priv->command, g_str_has_prefix (command, "AT") ? 0 : 2); + g_string_append (priv->command, command); + + if (command[strlen (command)] != '\r') + g_string_append_c (priv->command, '\r'); + + serial_debug (self, "-->", priv->command->str, -1); + + /* Only accept about 3 seconds of EAGAIN */ + if (priv->send_delay > 0) + eagain_count = 3000000 / priv->send_delay; + + s = priv->command->str; + while (*s) { + status = write (priv->fd, s, 1); + if (status < 0) { + if (errno == EAGAIN) { + eagain_count--; + if (eagain_count <= 0) { + g_set_error (error, MM_SERIAL_ERROR, MM_SERIAL_SEND_FAILED, + "Sending command failed: '%s'", strerror (errno)); + break; + } + } else { + g_set_error (error, MM_SERIAL_ERROR, MM_SERIAL_SEND_FAILED, + "Sending command failed: '%s'", strerror (errno)); + break; + } + } else + s++; + + if (priv->send_delay) + usleep (priv->send_delay); + } + + return *s == '\0'; +} + +typedef struct { + char *command; + MMSerialResponseFn callback; + gpointer user_data; + guint32 timeout; + gboolean cached; +} MMQueueData; + +static void +mm_serial_port_schedule_queue_process (MMSerialPort *self) +{ + MMSerialPortPrivate *priv = MM_SERIAL_PORT_GET_PRIVATE (self); + GSource *source; + + if (priv->timeout_id) { + /* A command is already in progress */ + return; + } + + if (priv->queue_schedule) { + /* Already scheduled */ + return; + } + + source = g_idle_source_new (); + g_source_set_closure (source, g_cclosure_new_object (G_CALLBACK (mm_serial_port_queue_process), G_OBJECT (self))); + g_source_attach (source, NULL); + priv->queue_schedule = g_source_get_id (source); + g_source_unref (source); +} + +static void +mm_serial_port_got_response (MMSerialPort *self, GError *error) +{ + MMSerialPortPrivate *priv = MM_SERIAL_PORT_GET_PRIVATE (self); + MMQueueData *info; + + if (priv->timeout_id) { + g_source_remove (priv->timeout_id); + priv->timeout_id = 0; + } + + info = (MMQueueData *) g_queue_pop_head (priv->queue); + if (info) { + if (info->cached && !error) + mm_serial_port_set_cached_reply (self, info->command, priv->response->str); + + if (info->callback) + info->callback (self, priv->response, error, info->user_data); + + g_free (info->command); + g_slice_free (MMQueueData, info); + } + + if (error) + g_error_free (error); + + g_string_truncate (priv->response, 0); + if (!g_queue_is_empty (priv->queue)) + mm_serial_port_schedule_queue_process (self); +} + +static gboolean +mm_serial_port_timed_out (gpointer data) +{ + MMSerialPort *self = MM_SERIAL_PORT (data); + MMSerialPortPrivate *priv = MM_SERIAL_PORT_GET_PRIVATE (self); + GError *error; + + priv->timeout_id = 0; + + error = g_error_new_literal (MM_SERIAL_ERROR, + MM_SERIAL_RESPONSE_TIMEOUT, + "Serial command timed out"); + /* FIXME: This is not completely correct - if the response finally arrives and there's + some other command waiting for response right now, the other command will + get the output of the timed out command. Not sure what to do here. */ + mm_serial_port_got_response (self, error); + + return FALSE; +} + +static gboolean +mm_serial_port_queue_process (gpointer data) +{ + MMSerialPort *self = MM_SERIAL_PORT (data); + MMSerialPortPrivate *priv = MM_SERIAL_PORT_GET_PRIVATE (self); + MMQueueData *info; + GError *error = NULL; + + priv->queue_schedule = 0; + + info = (MMQueueData *) g_queue_peek_head (priv->queue); + if (!info) + return FALSE; + + if (info->cached) { + const char *cached = mm_serial_port_get_cached_reply (self, info->command); + + if (cached) { + g_string_append (priv->response, cached); + mm_serial_port_got_response (self, NULL); + return FALSE; + } + } + + if (mm_serial_port_send_command (self, info->command, &error)) { + GSource *source; + + source = g_timeout_source_new_seconds (info->timeout); + g_source_set_closure (source, g_cclosure_new_object (G_CALLBACK (mm_serial_port_timed_out), G_OBJECT (self))); + g_source_attach (source, NULL); + priv->timeout_id = g_source_get_id (source); + g_source_unref (source); + } else { + mm_serial_port_got_response (self, error); + } + + return FALSE; +} + +void +mm_serial_port_add_unsolicited_msg_handler (MMSerialPort *self, + GRegex *regex, + MMSerialUnsolicitedMsgFn callback, + gpointer user_data, + GDestroyNotify notify) +{ + MMUnsolicitedMsgHandler *handler; + MMSerialPortPrivate *priv; + + g_return_if_fail (MM_IS_SERIAL_PORT (self)); + g_return_if_fail (regex != NULL); + + handler = g_slice_new (MMUnsolicitedMsgHandler); + handler->regex = g_regex_ref (regex); + handler->callback = callback; + handler->user_data = user_data; + handler->notify = notify; + + priv = MM_SERIAL_PORT_GET_PRIVATE (self); + priv->unsolicited_msg_handlers = g_slist_append (priv->unsolicited_msg_handlers, handler); +} + +void +mm_serial_port_set_response_parser (MMSerialPort *self, + MMSerialResponseParserFn fn, + gpointer user_data, + GDestroyNotify notify) +{ + MMSerialPortPrivate *priv = MM_SERIAL_PORT_GET_PRIVATE (self); + + g_return_if_fail (MM_IS_SERIAL_PORT (self)); + + if (priv->response_parser_notify) + priv->response_parser_notify (priv->response_parser_user_data); + + priv->response_parser_fn = fn; + priv->response_parser_user_data = user_data; + priv->response_parser_notify = notify; +} + +static gboolean +remove_eval_cb (const GMatchInfo *match_info, + GString *result, + gpointer user_data) +{ + int *result_len = (int *) user_data; + int start; + int end; + + if (g_match_info_fetch_pos (match_info, 0, &start, &end)) + *result_len -= (end - start); + + return FALSE; +} + +static void +parse_unsolicited_messages (MMSerialPort *self, + GString *response) +{ + MMSerialPortPrivate *priv = MM_SERIAL_PORT_GET_PRIVATE (self); + GSList *iter; + + for (iter = priv->unsolicited_msg_handlers; iter; iter = iter->next) { + MMUnsolicitedMsgHandler *handler = (MMUnsolicitedMsgHandler *) iter->data; + GMatchInfo *match_info; + gboolean matches; + + matches = g_regex_match_full (handler->regex, response->str, response->len, 0, 0, &match_info, NULL); + if (handler->callback) { + while (g_match_info_matches (match_info)) { + handler->callback (self, match_info, handler->user_data); + g_match_info_next (match_info, NULL); + } + } + + g_match_info_free (match_info); + + if (matches) { + /* Remove matches */ + char *str; + int result_len = response->len; + + str = g_regex_replace_eval (handler->regex, response->str, response->len, 0, 0, + remove_eval_cb, &result_len, NULL); + + g_string_truncate (response, 0); + g_string_append_len (response, str, result_len); + g_free (str); + } + } +} + +static gboolean +parse_response (MMSerialPort *self, + GString *response, + GError **error) +{ + MMSerialPortPrivate *priv = MM_SERIAL_PORT_GET_PRIVATE (self); + + g_return_val_if_fail (priv->response_parser_fn != NULL, FALSE); + + parse_unsolicited_messages (self, response); + + return priv->response_parser_fn (priv->response_parser_user_data, response, error); +} + +static gboolean +data_available (GIOChannel *source, + GIOCondition condition, + gpointer data) +{ + MMSerialPort *self = MM_SERIAL_PORT (data); + MMSerialPortPrivate *priv = MM_SERIAL_PORT_GET_PRIVATE (self); + char buf[SERIAL_BUF_SIZE + 1]; + gsize bytes_read; + GIOStatus status; + + if (condition & G_IO_HUP) { + g_string_truncate (priv->response, 0); + mm_serial_port_close (self); + return FALSE; + } + + if (condition & G_IO_ERR) { + g_string_truncate (priv->response, 0); + return TRUE; + } + + do { + GError *err = NULL; + + status = g_io_channel_read_chars (source, buf, SERIAL_BUF_SIZE, &bytes_read, &err); + if (status == G_IO_STATUS_ERROR) { + g_warning ("%s", err->message); + g_error_free (err); + err = NULL; + } + + /* If no bytes read, just let g_io_channel wait for more data */ + if (bytes_read == 0) + break; + + if (bytes_read > 0) { + serial_debug (self, "<--", buf, bytes_read); + g_string_append_len (priv->response, buf, bytes_read); + } + + /* Make sure the string doesn't grow too long */ + if (priv->response->len > SERIAL_BUF_SIZE) { + g_warning ("%s (%s): response buffer filled before repsonse received", + G_STRFUNC, mm_port_get_device (MM_PORT (self))); + g_string_erase (priv->response, 0, (SERIAL_BUF_SIZE / 2)); + } + + if (parse_response (self, priv->response, &err)) + mm_serial_port_got_response (self, err); + } while (bytes_read == SERIAL_BUF_SIZE || status == G_IO_STATUS_AGAIN); + + return TRUE; +} + +static void +port_connected (MMSerialPort *self, GParamSpec *pspec, gpointer user_data) +{ + MMSerialPortPrivate *priv = MM_SERIAL_PORT_GET_PRIVATE (self); + gboolean connected; + + if (priv->fd < 0) + return; + + /* When the port is connected, drop the serial port lock so PPP can do + * something with the port. When the port is disconnected, grab the lock + * again. + */ + connected = mm_port_get_connected (MM_PORT (self)); + + if (ioctl (priv->fd, (connected ? TIOCNXCL : TIOCEXCL)) < 0) { + g_warning ("%s: (%s) could not %s serial port lock: (%d) %s", + __func__, + mm_port_get_device (MM_PORT (self)), + connected ? "drop" : "re-acquire", + errno, + strerror (errno)); + if (!connected) { + // FIXME: do something here, maybe try again in a few seconds or + // close the port and error out? + } + } +} + +gboolean +mm_serial_port_open (MMSerialPort *self, GError **error) +{ + MMSerialPortPrivate *priv; + char *devfile; + const char *device; + + g_return_val_if_fail (MM_IS_SERIAL_PORT (self), FALSE); + + priv = MM_SERIAL_PORT_GET_PRIVATE (self); + + if (priv->fd >= 0) { + /* Already open */ + return TRUE; + } + + device = mm_port_get_device (MM_PORT (self)); + + g_message ("(%s) opening serial device...", device); + devfile = g_strdup_printf ("/dev/%s", device); + errno = 0; + priv->fd = open (devfile, O_RDWR | O_EXCL | O_NONBLOCK | O_NOCTTY); + g_free (devfile); + + if (priv->fd < 0) { + /* nozomi isn't ready yet when the port appears, and it'll return + * ENODEV when open(2) is called on it. Make sure we can handle this + * by returning a special error in that case. + */ + g_set_error (error, + MM_SERIAL_ERROR, + (errno == ENODEV) ? MM_SERIAL_OPEN_FAILED_NO_DEVICE : MM_SERIAL_OPEN_FAILED, + "Could not open serial device %s: %s", device, strerror (errno)); + return FALSE; + } + + if (ioctl (priv->fd, TIOCEXCL) < 0) { + g_set_error (error, MM_SERIAL_ERROR, MM_SERIAL_OPEN_FAILED, + "Could not lock serial device %s: %s", device, strerror (errno)); + close (priv->fd); + priv->fd = -1; + return FALSE; + } + + if (ioctl (priv->fd, TCGETA, &priv->old_t) < 0) { + g_set_error (error, MM_SERIAL_ERROR, MM_SERIAL_OPEN_FAILED, + "Could not open serial device %s: %s", device, strerror (errno)); + close (priv->fd); + priv->fd = -1; + return FALSE; + } + + if (!config_fd (self, error)) { + close (priv->fd); + priv->fd = -1; + return FALSE; + } + + priv->channel = g_io_channel_unix_new (priv->fd); + g_io_channel_set_encoding (priv->channel, NULL, NULL); + priv->watch_id = g_io_add_watch (priv->channel, + G_IO_IN | G_IO_ERR | G_IO_HUP, + data_available, self); + + g_warn_if_fail (priv->connected_id == 0); + priv->connected_id = g_signal_connect (self, "notify::" MM_PORT_CONNECTED, + G_CALLBACK (port_connected), NULL); + + return TRUE; +} + +void +mm_serial_port_close (MMSerialPort *self) +{ + MMSerialPortPrivate *priv; + + g_return_if_fail (MM_IS_SERIAL_PORT (self)); + + priv = MM_SERIAL_PORT_GET_PRIVATE (self); + + if (priv->connected_id) { + g_signal_handler_disconnect (self, priv->connected_id); + priv->connected_id = 0; + } + + if (priv->fd >= 0) { + g_message ("(%s) closing serial device...", mm_port_get_device (MM_PORT (self))); + + mm_port_set_connected (MM_PORT (self), FALSE); + + if (priv->channel) { + g_source_remove (priv->watch_id); + g_io_channel_shutdown (priv->channel, TRUE, NULL); + g_io_channel_unref (priv->channel); + priv->channel = NULL; + } + + if (priv->flash_id > 0) { + g_source_remove (priv->flash_id); + priv->flash_id = 0; + } + + ioctl (priv->fd, TCSETA, &priv->old_t); + close (priv->fd); + priv->fd = -1; + } +} + +static void +internal_queue_command (MMSerialPort *self, + const char *command, + gboolean cached, + guint32 timeout_seconds, + MMSerialResponseFn callback, + gpointer user_data) +{ + MMSerialPortPrivate *priv = MM_SERIAL_PORT_GET_PRIVATE (self); + MMQueueData *info; + + g_return_if_fail (MM_IS_SERIAL_PORT (self)); + g_return_if_fail (command != NULL); + + info = g_slice_new0 (MMQueueData); + info->command = g_strdup (command); + info->cached = cached; + info->timeout = timeout_seconds; + info->callback = callback; + info->user_data = user_data; + + /* Clear the cached value for this command if not asking for cached value */ + if (!cached) + mm_serial_port_set_cached_reply (self, command, NULL); + + g_queue_push_tail (priv->queue, info); + + if (g_queue_get_length (priv->queue) == 1) + mm_serial_port_schedule_queue_process (self); +} + +void +mm_serial_port_queue_command (MMSerialPort *self, + const char *command, + guint32 timeout_seconds, + MMSerialResponseFn callback, + gpointer user_data) +{ + internal_queue_command (self, command, FALSE, timeout_seconds, callback, user_data); +} + +void +mm_serial_port_queue_command_cached (MMSerialPort *self, + const char *command, + guint32 timeout_seconds, + MMSerialResponseFn callback, + gpointer user_data) +{ + internal_queue_command (self, command, TRUE, timeout_seconds, callback, user_data); +} + +typedef struct { + MMSerialPort *port; + speed_t current_speed; + MMSerialFlashFn callback; + gpointer user_data; +} FlashInfo; + +static gboolean +get_speed (MMSerialPort *self, speed_t *speed, GError **error) +{ + struct termios options; + + memset (&options, 0, sizeof (struct termios)); + if (tcgetattr (MM_SERIAL_PORT_GET_PRIVATE (self)->fd, &options) != 0) { + g_set_error (error, + MM_MODEM_ERROR, + MM_MODEM_ERROR_GENERAL, + "%s: tcgetattr() error %d", + __func__, errno); + return FALSE; + } + + *speed = cfgetospeed (&options); + return TRUE; +} + +static gboolean +set_speed (MMSerialPort *self, speed_t speed, GError **error) +{ + struct termios options; + int fd, count = 4; + gboolean success = FALSE; + + fd = MM_SERIAL_PORT_GET_PRIVATE (self)->fd; + + memset (&options, 0, sizeof (struct termios)); + if (tcgetattr (fd, &options) != 0) { + g_set_error (error, + MM_MODEM_ERROR, + MM_MODEM_ERROR_GENERAL, + "%s: tcgetattr() error %d", + __func__, errno); + return FALSE; + } + + cfsetispeed (&options, speed); + cfsetospeed (&options, speed); + options.c_cflag |= (CLOCAL | CREAD); + + while (count-- > 0) { + if (tcsetattr (fd, TCSANOW, &options) == 0) { + success = TRUE; + break; /* Operation successful */ + } + + /* Try a few times if EAGAIN */ + if (errno == EAGAIN) + g_usleep (100000); + else { + /* If not EAGAIN, hard error */ + g_set_error (error, + MM_MODEM_ERROR, + MM_MODEM_ERROR_GENERAL, + "%s: tcsetattr() error %d", + __func__, errno); + return FALSE; + } + } + + if (!success) { + g_set_error (error, + MM_MODEM_ERROR, + MM_MODEM_ERROR_GENERAL, + "%s: tcsetattr() retry timeout", + __func__); + return FALSE; + } + + return TRUE; +} + +static gboolean +flash_do (gpointer data) +{ + FlashInfo *info = (FlashInfo *) data; + MMSerialPortPrivate *priv = MM_SERIAL_PORT_GET_PRIVATE (info->port); + GError *error = NULL; + + priv->flash_id = 0; + + if (!set_speed (info->port, info->current_speed, &error)) + g_assert (error); + + info->callback (info->port, error, info->user_data); + g_clear_error (&error); + g_slice_free (FlashInfo, info); + return FALSE; +} + +gboolean +mm_serial_port_flash (MMSerialPort *self, + guint32 flash_time, + MMSerialFlashFn callback, + gpointer user_data) +{ + FlashInfo *info; + MMSerialPortPrivate *priv; + speed_t cur_speed = 0; + GError *error = NULL; + + g_return_val_if_fail (MM_IS_SERIAL_PORT (self), FALSE); + g_return_val_if_fail (callback != NULL, FALSE); + + priv = MM_SERIAL_PORT_GET_PRIVATE (self); + + if (priv->flash_id > 0) { + error = g_error_new_literal (MM_MODEM_ERROR, + MM_MODEM_ERROR_OPERATION_IN_PROGRESS, + "Modem is already being flashed."); + callback (self, error, user_data); + g_error_free (error); + return FALSE; + } + + if (!get_speed (self, &cur_speed, &error)) { + callback (self, error, user_data); + g_error_free (error); + return FALSE; + } + + info = g_slice_new0 (FlashInfo); + info->port = self; + info->current_speed = cur_speed; + info->callback = callback; + info->user_data = user_data; + + if (!set_speed (self, B0, &error)) { + callback (self, error, user_data); + g_error_free (error); + return FALSE; + } + + priv->flash_id = g_timeout_add (flash_time, flash_do, info); + return TRUE; +} + +void +mm_serial_port_flash_cancel (MMSerialPort *self) +{ + MMSerialPortPrivate *priv; + + g_return_if_fail (MM_IS_SERIAL_PORT (self)); + + priv = MM_SERIAL_PORT_GET_PRIVATE (self); + + if (priv->flash_id > 0) { + g_source_remove (priv->flash_id); + priv->flash_id = 0; + } +} + +/*****************************************************************************/ + +MMSerialPort * +mm_serial_port_new (const char *name, MMPortType ptype) +{ + return MM_SERIAL_PORT (g_object_new (MM_TYPE_SERIAL_PORT, + MM_PORT_DEVICE, name, + MM_PORT_SUBSYS, MM_PORT_SUBSYS_TTY, + MM_PORT_TYPE, ptype, + NULL)); +} + +static void +mm_serial_port_init (MMSerialPort *self) +{ + MMSerialPortPrivate *priv = MM_SERIAL_PORT_GET_PRIVATE (self); + + priv->reply_cache = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free); + + priv->fd = -1; + priv->baud = 57600; + priv->bits = 8; + priv->parity = 'n'; + priv->stopbits = 1; + priv->send_delay = 1000; + + priv->queue = g_queue_new (); + priv->command = g_string_new_len ("AT", SERIAL_BUF_SIZE); + priv->response = g_string_sized_new (SERIAL_BUF_SIZE); +} + +static void +set_property (GObject *object, guint prop_id, + const GValue *value, GParamSpec *pspec) +{ + MMSerialPortPrivate *priv = MM_SERIAL_PORT_GET_PRIVATE (object); + + switch (prop_id) { + case PROP_BAUD: + priv->baud = g_value_get_uint (value); + break; + case PROP_BITS: + priv->bits = g_value_get_uint (value); + break; + case PROP_PARITY: + priv->parity = g_value_get_char (value); + break; + case PROP_STOPBITS: + priv->stopbits = g_value_get_uint (value); + break; + case PROP_SEND_DELAY: + priv->send_delay = g_value_get_uint64 (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +get_property (GObject *object, guint prop_id, + GValue *value, GParamSpec *pspec) +{ + MMSerialPortPrivate *priv = MM_SERIAL_PORT_GET_PRIVATE (object); + + switch (prop_id) { + case PROP_BAUD: + g_value_set_uint (value, priv->baud); + break; + case PROP_BITS: + g_value_set_uint (value, priv->bits); + break; + case PROP_PARITY: + g_value_set_char (value, priv->parity); + break; + case PROP_STOPBITS: + g_value_set_uint (value, priv->stopbits); + break; + case PROP_SEND_DELAY: + g_value_set_uint64 (value, priv->send_delay); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +dispose (GObject *object) +{ + mm_serial_port_close (MM_SERIAL_PORT (object)); + + G_OBJECT_CLASS (mm_serial_port_parent_class)->dispose (object); +} + +static void +finalize (GObject *object) +{ + MMSerialPort *self = MM_SERIAL_PORT (object); + MMSerialPortPrivate *priv = MM_SERIAL_PORT_GET_PRIVATE (self); + + g_hash_table_destroy (priv->reply_cache); + g_queue_free (priv->queue); + g_string_free (priv->command, TRUE); + g_string_free (priv->response, TRUE); + + while (priv->unsolicited_msg_handlers) { + MMUnsolicitedMsgHandler *handler = (MMUnsolicitedMsgHandler *) priv->unsolicited_msg_handlers->data; + + if (handler->notify) + handler->notify (handler->user_data); + + g_regex_unref (handler->regex); + g_slice_free (MMUnsolicitedMsgHandler, handler); + priv->unsolicited_msg_handlers = g_slist_delete_link (priv->unsolicited_msg_handlers, + priv->unsolicited_msg_handlers); + } + + if (priv->response_parser_notify) + priv->response_parser_notify (priv->response_parser_user_data); + + G_OBJECT_CLASS (mm_serial_port_parent_class)->finalize (object); +} + +static void +mm_serial_port_class_init (MMSerialPortClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + g_type_class_add_private (object_class, sizeof (MMSerialPortPrivate)); + + /* Virtual methods */ + object_class->set_property = set_property; + object_class->get_property = get_property; + object_class->dispose = dispose; + object_class->finalize = finalize; + + /* Properties */ + g_object_class_install_property + (object_class, PROP_BAUD, + g_param_spec_uint (MM_SERIAL_PORT_BAUD, + "Baud", + "Baud rate", + 0, G_MAXUINT, 57600, + G_PARAM_READWRITE)); + + g_object_class_install_property + (object_class, PROP_BITS, + g_param_spec_uint (MM_SERIAL_PORT_BITS, + "Bits", + "Bits", + 5, 8, 8, + G_PARAM_READWRITE)); + + g_object_class_install_property + (object_class, PROP_PARITY, + g_param_spec_char (MM_SERIAL_PORT_PARITY, + "Parity", + "Parity", + 'E', 'o', 'n', + G_PARAM_READWRITE)); + + g_object_class_install_property + (object_class, PROP_STOPBITS, + g_param_spec_uint (MM_SERIAL_PORT_STOPBITS, + "Stopbits", + "Stopbits", + 1, 2, 1, + G_PARAM_READWRITE)); + + g_object_class_install_property + (object_class, PROP_SEND_DELAY, + g_param_spec_uint64 (MM_SERIAL_PORT_SEND_DELAY, + "SendDelay", + "Send delay", + 0, G_MAXUINT64, 0, + G_PARAM_READWRITE)); +} diff --git a/src/mm-serial-port.h b/src/mm-serial-port.h new file mode 100644 index 0000000..841b4fa --- /dev/null +++ b/src/mm-serial-port.h @@ -0,0 +1,105 @@ +/* -*- 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_SERIAL_PORT_H +#define MM_SERIAL_PORT_H + +#include <glib.h> +#include <glib/gtypes.h> +#include <glib-object.h> + +#include "mm-port.h" + +#define MM_TYPE_SERIAL_PORT (mm_serial_port_get_type ()) +#define MM_SERIAL_PORT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_SERIAL_PORT, MMSerialPort)) +#define MM_SERIAL_PORT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_SERIAL_PORT, MMSerialPortClass)) +#define MM_IS_SERIAL_PORT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_SERIAL_PORT)) +#define MM_IS_SERIAL_PORT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_SERIAL_PORT)) +#define MM_SERIAL_PORT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_SERIAL_PORT, MMSerialPortClass)) + +#define MM_SERIAL_PORT_BAUD "baud" +#define MM_SERIAL_PORT_BITS "bits" +#define MM_SERIAL_PORT_PARITY "parity" +#define MM_SERIAL_PORT_STOPBITS "stopbits" +#define MM_SERIAL_PORT_SEND_DELAY "send-delay" + +typedef struct _MMSerialPort MMSerialPort; +typedef struct _MMSerialPortClass MMSerialPortClass; + +typedef gboolean (*MMSerialResponseParserFn) (gpointer user_data, + GString *response, + GError **error); + +typedef void (*MMSerialUnsolicitedMsgFn) (MMSerialPort *port, + GMatchInfo *match_info, + gpointer user_data); + +typedef void (*MMSerialResponseFn) (MMSerialPort *port, + GString *response, + GError *error, + gpointer user_data); + +typedef void (*MMSerialFlashFn) (MMSerialPort *port, + GError *error, + gpointer user_data); + +struct _MMSerialPort { + MMPort parent; +}; + +struct _MMSerialPortClass { + MMPortClass parent; +}; + +GType mm_serial_port_get_type (void); + +MMSerialPort *mm_serial_port_new (const char *name, MMPortType ptype); + +void mm_serial_port_add_unsolicited_msg_handler (MMSerialPort *self, + GRegex *regex, + MMSerialUnsolicitedMsgFn callback, + gpointer user_data, + GDestroyNotify notify); + +void mm_serial_port_set_response_parser (MMSerialPort *self, + MMSerialResponseParserFn fn, + gpointer user_data, + GDestroyNotify notify); + +gboolean mm_serial_port_open (MMSerialPort *self, + GError **error); + +void mm_serial_port_close (MMSerialPort *self); +void mm_serial_port_queue_command (MMSerialPort *self, + const char *command, + guint32 timeout_seconds, + MMSerialResponseFn callback, + gpointer user_data); + +void mm_serial_port_queue_command_cached (MMSerialPort *self, + const char *command, + guint32 timeout_seconds, + MMSerialResponseFn callback, + gpointer user_data); + +gboolean mm_serial_port_flash (MMSerialPort *self, + guint32 flash_time, + MMSerialFlashFn callback, + gpointer user_data); +void mm_serial_port_flash_cancel (MMSerialPort *self); + +#endif /* MM_SERIAL_PORT_H */ + diff --git a/src/tests/Makefile.am b/src/tests/Makefile.am new file mode 100644 index 0000000..74255db --- /dev/null +++ b/src/tests/Makefile.am @@ -0,0 +1,22 @@ +INCLUDES = \ + -I$(top_srcdir)/src + +noinst_PROGRAMS = test-modem-helpers + +test_modem_helpers_SOURCES = \ + test-modem-helpers.c + +test_modem_helpers_CPPFLAGS = \ + $(MM_CFLAGS) + +test_modem_helpers_LDADD = \ + $(top_builddir)/src/libmodem-helpers.la \ + $(MM_LIBS) + +if WITH_TESTS + +check-local: test-modem-helpers + $(abs_builddir)/test-modem-helpers + +endif + diff --git a/src/tests/test-modem-helpers.c b/src/tests/test-modem-helpers.c new file mode 100644 index 0000000..3d93423 --- /dev/null +++ b/src/tests/test-modem-helpers.c @@ -0,0 +1,479 @@ +/* -*- 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. + */ + +#include <glib.h> +#include <string.h> + +#include "mm-modem-helpers.h" + +#define MM_SCAN_TAG_STATUS "status" +#define MM_SCAN_TAG_OPER_LONG "operator-long" +#define MM_SCAN_TAG_OPER_SHORT "operator-short" +#define MM_SCAN_TAG_OPER_NUM "operator-num" +#define MM_SCAN_TAG_ACCESS_TECH "access-tech" + +typedef struct { + const char *status; + const char *oper_long; + const char *oper_short; + const char *oper_num; + const char *tech; +} OperEntry; + +#define ARRAY_LEN(i) (sizeof (i) / sizeof (i[0])) + +static void +test_results (const char *desc, + const char *reply, + OperEntry *expected_results, + guint32 expected_results_len) +{ + guint i; + GError *error = NULL; + GPtrArray *results; + + g_print ("\nTesting %s +COPS response...\n", desc); + + results = mm_gsm_parse_scan_response (reply, &error); + g_assert (results); + g_assert (error == NULL); + + g_assert (results->len == expected_results_len); + + for (i = 0; i < results->len; i++) { + GHashTable *entry = g_ptr_array_index (results, i); + const char *value; + OperEntry *expected = &expected_results[i]; + + value = g_hash_table_lookup (entry, MM_SCAN_TAG_STATUS); + g_assert (value); + g_assert (strcmp (value, expected->status) == 0); + + value = g_hash_table_lookup (entry, MM_SCAN_TAG_OPER_LONG); + if (expected->oper_long) { + g_assert (value); + g_assert (strcmp (value, expected->oper_long) == 0); + } else + g_assert (value == NULL); + + value = g_hash_table_lookup (entry, MM_SCAN_TAG_OPER_SHORT); + if (expected->oper_short) { + g_assert (value); + g_assert (strcmp (value, expected->oper_short) == 0); + } else + g_assert (value == NULL); + + value = g_hash_table_lookup (entry, MM_SCAN_TAG_OPER_NUM); + g_assert (expected->oper_num); + g_assert (value); + g_assert (strcmp (value, expected->oper_num) == 0); + + value = g_hash_table_lookup (entry, MM_SCAN_TAG_ACCESS_TECH); + if (expected->tech) { + g_assert (value); + g_assert (strcmp (value, expected->tech) == 0); + } else + g_assert (value == NULL); + } + + mm_gsm_destroy_scan_data (results); +} + +static void +test_cops_response_tm506 (void *f, gpointer d) +{ + const char *reply = "+COPS: (2,\"\",\"T-Mobile\",\"31026\",0),(2,\"T - Mobile\",\"T - Mobile\",\"310260\"),2),(1,\"AT&T\",\"AT&T\",\"310410\"),0)"; + static OperEntry expected[] = { + { "2", NULL, "T-Mobile", "31026", "0" }, + { "2", "T - Mobile", "T - Mobile", "310260", "2" }, + { "1", "AT&T", "AT&T", "310410", "0" } + }; + + test_results ("TM-506", reply, &expected[0], ARRAY_LEN (expected)); +} + +static void +test_cops_response_gt3gplus (void *f, gpointer d) +{ + const char *reply = "+COPS: (1,\"T-Mobile US\",\"TMO US\",\"31026\",0),(1,\"Cingular\",\"Cingular\",\"310410\",0),,(0, 1, 3),(0-2)"; + static OperEntry expected[] = { + { "1", "T-Mobile US", "TMO US", "31026", "0" }, + { "1", "Cingular", "Cingular", "310410", "0" }, + }; + + test_results ("GlobeTrotter 3G+ (nozomi)", reply, &expected[0], ARRAY_LEN (expected)); +} + +static void +test_cops_response_ac881 (void *f, gpointer d) +{ + const char *reply = "+COPS: (1,\"T-Mobile\",\"TMO\",\"31026\",0),(1,\"AT&T\",\"AT&T\",\"310410\",2),(1,\"AT&T\",\"AT&T\",\"310410\",0),,(0,1,2,3,4),)"; + static OperEntry expected[] = { + { "1", "T-Mobile", "TMO", "31026", "0" }, + { "1", "AT&T", "AT&T", "310410", "2" }, + { "1", "AT&T", "AT&T", "310410", "0" }, + }; + + test_results ("Sierra AirCard 881", reply, &expected[0], ARRAY_LEN (expected)); +} + +static void +test_cops_response_gtmax36 (void *f, gpointer d) +{ + const char *reply = "+COPS: (2,\"T-Mobile US\",\"TMO US\",\"31026\",0),(1,\"AT&T\",\"AT&T\",\"310410\",2),(1,\"AT&T\",\"AT&T\",\"310410\",0),,(0, 1,)"; + static OperEntry expected[] = { + { "2", "T-Mobile US", "TMO US", "31026", "0" }, + { "1", "AT&T", "AT&T", "310410", "2" }, + { "1", "AT&T", "AT&T", "310410", "0" }, + }; + + test_results ("Option GlobeTrotter MAX 3.6", reply, &expected[0], ARRAY_LEN (expected)); +} + +static void +test_cops_response_ac860 (void *f, gpointer d) +{ + const char *reply = "+COPS: (2,\"T-Mobile\",\"TMO\",\"31026\",0),(1,\"Cingular\",\"Cinglr\",\"310410\",2),(1,\"Cingular\",\"Cinglr\",\"310410\",0),,)"; + static OperEntry expected[] = { + { "2", "T-Mobile", "TMO", "31026", "0" }, + { "1", "Cingular", "Cinglr", "310410", "2" }, + { "1", "Cingular", "Cinglr", "310410", "0" }, + }; + + test_results ("Sierra AirCard 860", reply, &expected[0], ARRAY_LEN (expected)); +} + +static void +test_cops_response_gtm378 (void *f, gpointer d) +{ + const char *reply = "+COPS: (2,\"T-Mobile\",\"T-Mobile\",\"31026\",0),(1,\"AT&T\",\"AT&T\",\"310410\",2),(1,\"AT&T\",\"AT&T\",\"310410\",0),,(0, 1, 3),(0-2)"; + static OperEntry expected[] = { + { "2", "T-Mobile", "T-Mobile", "31026", "0" }, + { "1", "AT&T", "AT&T", "310410", "2" }, + { "1", "AT&T", "AT&T", "310410", "0" }, + }; + + test_results ("Option GTM378", reply, &expected[0], ARRAY_LEN (expected)); +} + +static void +test_cops_response_motoc (void *f, gpointer d) +{ + const char *reply = "+COPS: (2,\"T-Mobile\",\"\",\"310260\"),(0,\"Cingular Wireless\",\"\",\"310410\")"; + static OperEntry expected[] = { + { "2", "T-Mobile", NULL, "310260", NULL }, + { "0", "Cingular Wireless", NULL, "310410", NULL }, + }; + + test_results ("BUSlink SCWi275u (Motorola C-series)", reply, &expected[0], ARRAY_LEN (expected)); +} + +static void +test_cops_response_mf627a (void *f, gpointer d) +{ + const char *reply = "+COPS: (2,\"AT&T@\",\"AT&TD\",\"310410\",0),(3,\"Voicestream Wireless Corporation\",\"VSTREAM\",\"31026\",0),"; + static OperEntry expected[] = { + { "2", "AT&T@", "AT&TD", "310410", "0" }, + { "3", "Voicestream Wireless Corporation", "VSTREAM", "31026", "0" }, + }; + + test_results ("ZTE MF627 (A)", reply, &expected[0], ARRAY_LEN (expected)); +} + +static void +test_cops_response_mf627b (void *f, gpointer d) +{ + const char *reply = "+COPS: (2,\"AT&Tp\",\"AT&T@\",\"310410\",0),(3,\"\",\"\",\"31026\",0),"; + static OperEntry expected[] = { + { "2", "AT&Tp", "AT&T@", "310410", "0" }, + { "3", NULL, NULL, "31026", "0" }, + }; + + test_results ("ZTE MF627 (B)", reply, &expected[0], ARRAY_LEN (expected)); +} + +static void +test_cops_response_e160g (void *f, gpointer d) +{ + const char *reply = "+COPS: (2,\"T-Mobile\",\"TMO\",\"31026\",0),(1,\"AT&T\",\"AT&T\",\"310410\",0),,(0,1,2,3,4),(0,1,2)"; + static OperEntry expected[] = { + { "2", "T-Mobile", "TMO", "31026", "0" }, + { "1", "AT&T", "AT&T", "310410", "0" }, + }; + + test_results ("Huawei E160G", reply, &expected[0], ARRAY_LEN (expected)); +} + +static void +test_cops_response_mercury (void *f, gpointer d) +{ + const char *reply = "+COPS: (2,\"\",\"\",\"310410\",2),(1,\"AT&T\",\"AT&T\",\"310410\",0),(1,\"T-Mobile\",\"TMO\",\"31026\",0),,(0,1,2,3,4),(0,1,2)"; + static OperEntry expected[] = { + { "2", NULL, NULL, "310410", "2" }, + { "1", "AT&T", "AT&T", "310410", "0" }, + { "1", "T-Mobile", "TMO", "31026", "0" }, + }; + + test_results ("Sierra AT&T USBConnect Mercury", reply, &expected[0], ARRAY_LEN (expected)); +} + +static void +test_cops_response_quicksilver (void *f, gpointer d) +{ + const char *reply = "+COPS: (2,\"AT&T\",\"\",\"310410\",0),(2,\"\",\"\",\"3104100\",2),(1,\"AT&T\",\"\",\"310260\",0),,(0-4),(0-2)"; + static OperEntry expected[] = { + { "2", "AT&T", NULL, "310410", "0" }, + { "2", NULL, NULL, "3104100", "2" }, + { "1", "AT&T", NULL, "310260", "0" }, + }; + + test_results ("Option AT&T Quicksilver", reply, &expected[0], ARRAY_LEN (expected)); +} + +static void +test_cops_response_icon225 (void *f, gpointer d) +{ + const char *reply = "+COPS: (2,\"T-Mobile US\",\"TMO US\",\"31026\",0),(1,\"AT&T\",\"AT&T\",\"310410\",0),,(0, 1, 3),(0-2)"; + static OperEntry expected[] = { + { "2", "T-Mobile US", "TMO US", "31026", "0" }, + { "1", "AT&T", "AT&T", "310410", "0" }, + }; + + test_results ("Option iCON 225", reply, &expected[0], ARRAY_LEN (expected)); +} + +static void +test_cops_response_icon452 (void *f, gpointer d) +{ + const char *reply = "+COPS: (1,\"T-Mobile US\",\"TMO US\",\"31026\",0),(2,\"T-Mobile\",\"T-Mobile\",\"310260\",2),(1,\"AT&T\",\"AT&T\",\"310410\",2),(1,\"AT&T\",\"AT&T\",\"310410\",0),,(0,1,2,3,4),(0,1,2)"; + static OperEntry expected[] = { + { "1", "T-Mobile US", "TMO US", "31026", "0" }, + { "2", "T-Mobile", "T-Mobile", "310260", "2" }, + { "1", "AT&T", "AT&T", "310410", "2" }, + { "1", "AT&T", "AT&T", "310410", "0" } + }; + + test_results ("Option iCON 452", reply, &expected[0], ARRAY_LEN (expected)); +} + +static void +test_cops_response_f3507g (void *f, gpointer d) +{ + const char *reply = "+COPS: (2,\"T - Mobile\",\"T - Mobile\",\"31026\",0),(1,\"AT&T\",\"AT&T\",\"310410\",0),(1,\"AT&T\",\"AT&T\",\"310410\",2)"; + static OperEntry expected[] = { + { "2", "T - Mobile", "T - Mobile", "31026", "0" }, + { "1", "AT&T", "AT&T", "310410", "0" }, + { "1", "AT&T", "AT&T", "310410", "2" } + }; + + test_results ("Ericsson F3507g", reply, &expected[0], ARRAY_LEN (expected)); +} + +static void +test_cops_response_f3607gw (void *f, gpointer d) +{ + const char *reply = "+COPS: (2,\"T - Mobile\",\"T - Mobile\",\"31026\",0),(1,\"AT&T\",\"AT&T\",\"310410\"),2),(1,\"AT&T\",\"AT&T\",\"310410\"),0)"; + static OperEntry expected[] = { + { "2", "T - Mobile", "T - Mobile", "31026", "0" }, + { "1", "AT&T", "AT&T", "310410", "2" }, + { "1", "AT&T", "AT&T", "310410", "0" } + }; + + test_results ("Ericsson F3607gw", reply, &expected[0], ARRAY_LEN (expected)); +} + +static void +test_cops_response_mc8775 (void *f, gpointer d) +{ + const char *reply = "+COPS: (2,\"T-Mobile\",\"T-Mobile\",\"31026\",0),(1,\"AT&T\",\"AT&T\",\"310410\",2),(1,\"AT&T\",\"AT&T\",\"310410\",0),,(0,1,2,3,4),(0,1,2)"; + static OperEntry expected[] = { + { "2", "T-Mobile", "T-Mobile", "31026", "0" }, + { "1", "AT&T", "AT&T", "310410", "2" }, + { "1", "AT&T", "AT&T", "310410", "0" } + }; + + test_results ("Sierra MC8775", reply, &expected[0], ARRAY_LEN (expected)); +} + +static void +test_cops_response_n80 (void *f, gpointer d) +{ + const char *reply = "+COPS: (2,\"T - Mobile\",,\"31026\"),(1,\"Einstein PCS\",,\"31064\"),(1,\"Cingular\",,\"31041\"),,(0,1,3),(0,2)"; + static OperEntry expected[] = { + { "2", "T - Mobile", NULL, "31026", NULL }, + { "1", "Einstein PCS", NULL, "31064", NULL }, + { "1", "Cingular", NULL, "31041", NULL }, + }; + + test_results ("Nokia N80", reply, &expected[0], ARRAY_LEN (expected)); +} + +static void +test_cops_response_e1550 (void *f, gpointer d) +{ + const char *reply = "+COPS: (2,\"T-Mobile\",\"TMO\",\"31026\",0),(1,\"AT&T\",\"AT&T\",\"310410\",0),,(0,1,2,3,4),(0,1,2)"; + static OperEntry expected[] = { + { "2", "T-Mobile", "TMO", "31026", "0" }, + { "1", "AT&T", "AT&T", "310410", "0" }, + }; + + test_results ("Huawei E1550", reply, &expected[0], ARRAY_LEN (expected)); +} + +static void +test_cops_response_mf622 (void *f, gpointer d) +{ + const char *reply = "+COPS: (2,\"T-Mobile\",\"T-Mobile\",\"31026\",0),(1,\"\",\"\",\"310410\",0),"; + static OperEntry expected[] = { + { "2", "T-Mobile", "T-Mobile", "31026", "0" }, + { "1", NULL, NULL, "310410", "0" }, + }; + + test_results ("ZTE MF622", reply, &expected[0], ARRAY_LEN (expected)); +} + +static void +test_cops_response_e226 (void *f, gpointer d) +{ + const char *reply = "+COPS: (1,\"\",\"\",\"31026\",0),(1,\"\",\"\",\"310410\",2),(1,\"\",\"\",\"310410\",0),,(0,1,3,4),(0,1,2)"; + static OperEntry expected[] = { + { "1", NULL, NULL, "31026", "0" }, + { "1", NULL, NULL, "310410", "2" }, + { "1", NULL, NULL, "310410", "0" }, + }; + + test_results ("Huawei E226", reply, &expected[0], ARRAY_LEN (expected)); +} + +static void +test_cops_response_xu870 (void *f, gpointer d) +{ + const char *reply = "+COPS: (0,\"AT&T MicroCell\",\"AT&T MicroCell\",\"310410\",2)\r\n+COPS: (1,\"AT&T MicroCell\",\"AT&T MicroCell\",\"310410\",0)\r\n+COPS: (1,\"T-Mobile\",\"TMO\",\"31026\",0)\r\n"; + static OperEntry expected[] = { + { "0", "AT&T MicroCell", "AT&T MicroCell", "310410", "2" }, + { "1", "AT&T MicroCell", "AT&T MicroCell", "310410", "0" }, + { "1", "T-Mobile", "TMO", "31026", "0" }, + }; + + test_results ("Novatel XU870", reply, &expected[0], ARRAY_LEN (expected)); +} + +static void +test_cops_response_gtultraexpress (void *f, gpointer d) +{ + const char *reply = "+COPS: (2,\"T-Mobile US\",\"TMO US\",\"31026\",0),(1,\"AT&T\",\"AT&T\",\"310410\",2),(1,\"AT&T\",\"AT&T\",\"310410\",0),,(0,1,2,3,4),(0,1,2)"; + static OperEntry expected[] = { + { "2", "T-Mobile US", "TMO US", "31026", "0" }, + { "1", "AT&T", "AT&T", "310410", "2" }, + { "1", "AT&T", "AT&T", "310410", "0" }, + }; + + test_results ("Option GlobeTrotter Ultra Express", reply, &expected[0], ARRAY_LEN (expected)); +} + +static void +test_cops_response_n2720 (void *f, gpointer d) +{ + const char *reply = "+COPS: (2,\"T - Mobile\",,\"31026\",0),\r\n(1,\"AT&T\",,\"310410\",0),,(0,1,3),(0,2)"; + static OperEntry expected[] = { + { "2", "T - Mobile", NULL, "31026", "0" }, + { "1", "AT&T", NULL, "310410", "0" }, + }; + + test_results ("Nokia 2720", reply, &expected[0], ARRAY_LEN (expected)); +} + +static void +test_cops_response_gobi (void *f, gpointer d) +{ + const char *reply = "+COPS: (2,\"T-Mobile\",\"T-Mobile\",\"31026\",0),(1,\"AT&T\",\"AT&T\",\"310410\",2),(1,\"AT&T\",\"AT&T\",\"310410\",0),,(0,1,2,3,4),(0,1,2)"; + static OperEntry expected[] = { + { "2", "T-Mobile", "T-Mobile", "31026", "0" }, + { "1", "AT&T", "AT&T", "310410", "2" }, + { "1", "AT&T", "AT&T", "310410", "0" }, + }; + + test_results ("Qualcomm Gobi", reply, &expected[0], ARRAY_LEN (expected)); +} + +static void +test_cops_response_gsm_invalid (void *f, gpointer d) +{ + const char *reply = "+COPS: (0,1,2,3),(1,2,3,4)"; + GPtrArray *results; + GError *error = NULL; + + results = mm_gsm_parse_scan_response (reply, &error); + g_assert (results != NULL); + g_assert (error == NULL); +} + +static void +test_cops_response_umts_invalid (void *f, gpointer d) +{ + const char *reply = "+COPS: (0,1,2,3,4),(1,2,3,4,5)"; + GPtrArray *results; + GError *error = NULL; + + results = mm_gsm_parse_scan_response (reply, &error); + g_assert (results != NULL); + g_assert (error == NULL); +} + + +typedef void (*TCFunc)(void); + +#define TESTCASE(t, d) g_test_create_case (#t, 0, d, NULL, (TCFunc) t, NULL) + +int main (int argc, char **argv) +{ + GTestSuite *suite; + + g_test_init (&argc, &argv, NULL); + + suite = g_test_get_root (); + + g_test_suite_add (suite, TESTCASE (test_cops_response_tm506, NULL)); + g_test_suite_add (suite, TESTCASE (test_cops_response_gt3gplus, NULL)); + g_test_suite_add (suite, TESTCASE (test_cops_response_ac881, NULL)); + g_test_suite_add (suite, TESTCASE (test_cops_response_gtmax36, NULL)); + g_test_suite_add (suite, TESTCASE (test_cops_response_ac860, NULL)); + g_test_suite_add (suite, TESTCASE (test_cops_response_gtm378, NULL)); + g_test_suite_add (suite, TESTCASE (test_cops_response_motoc, NULL)); + g_test_suite_add (suite, TESTCASE (test_cops_response_mf627a, NULL)); + g_test_suite_add (suite, TESTCASE (test_cops_response_mf627b, NULL)); + g_test_suite_add (suite, TESTCASE (test_cops_response_e160g, NULL)); + g_test_suite_add (suite, TESTCASE (test_cops_response_mercury, NULL)); + g_test_suite_add (suite, TESTCASE (test_cops_response_quicksilver, NULL)); + g_test_suite_add (suite, TESTCASE (test_cops_response_icon225, NULL)); + g_test_suite_add (suite, TESTCASE (test_cops_response_icon452, NULL)); + g_test_suite_add (suite, TESTCASE (test_cops_response_f3507g, NULL)); + g_test_suite_add (suite, TESTCASE (test_cops_response_f3607gw, NULL)); + g_test_suite_add (suite, TESTCASE (test_cops_response_mc8775, NULL)); + g_test_suite_add (suite, TESTCASE (test_cops_response_n80, NULL)); + g_test_suite_add (suite, TESTCASE (test_cops_response_e1550, NULL)); + g_test_suite_add (suite, TESTCASE (test_cops_response_mf622, NULL)); + g_test_suite_add (suite, TESTCASE (test_cops_response_e226, NULL)); + g_test_suite_add (suite, TESTCASE (test_cops_response_xu870, NULL)); + g_test_suite_add (suite, TESTCASE (test_cops_response_gtultraexpress, NULL)); + g_test_suite_add (suite, TESTCASE (test_cops_response_n2720, NULL)); + g_test_suite_add (suite, TESTCASE (test_cops_response_gobi, NULL)); + + g_test_suite_add (suite, TESTCASE (test_cops_response_gsm_invalid, NULL)); + g_test_suite_add (suite, TESTCASE (test_cops_response_umts_invalid, NULL)); + + return g_test_run (); +} + diff --git a/test/Makefile.am b/test/Makefile.am new file mode 100644 index 0000000..3d571c3 --- /dev/null +++ b/test/Makefile.am @@ -0,0 +1,23 @@ +if HAVE_PPPD_H + +pppd_plugindir = $(PPPD_PLUGIN_DIR) +pppd_plugin_LTLIBRARIES = mm-test-pppd-plugin.la + +mm_test_pppd_plugin_la_SOURCES = \ + mm-test-pppd-plugin.c + +mm_test_pppd_plugin_la_CPPFLAGS = $(MM_CFLAGS) +mm_test_pppd_plugin_la_LDFLAGS = -module -avoid-version +mm_test_pppd_plugin_la_LIBADD = $(MM_LIBS) + +endif + +noinst_PROGRAMS = lsudev +lsudev_SOURCES = lsudev.c +lsudev_CPPFLAGS = $(GUDEV_CFLAGS) +lsudev_LDADD = $(GUDEV_LIBS) + + +EXTRA_DIST = \ + mm-test.py + diff --git a/test/lsudev.c b/test/lsudev.c new file mode 100644 index 0000000..02d9ab2 --- /dev/null +++ b/test/lsudev.c @@ -0,0 +1,180 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; 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. + * + * 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., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright (C) 2009 Red Hat, Inc. + */ + +#include <glib.h> +#define G_UDEV_API_IS_SUBJECT_TO_CHANGE +#include <gudev/gudev.h> + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <stdarg.h> +#include <signal.h> + +static GMainLoop *loop = NULL; + +static void +signal_handler (int signo) +{ + if (signo == SIGINT || signo == SIGTERM) { + g_message ("Caught signal %d, shutting down...", signo); + g_main_loop_quit (loop); + } +} + +static void +setup_signals (void) +{ + struct sigaction action; + sigset_t mask; + + sigemptyset (&mask); + action.sa_handler = signal_handler; + action.sa_mask = mask; + action.sa_flags = 0; + sigaction (SIGTERM, &action, NULL); + sigaction (SIGINT, &action, NULL); +} + +static void +println (guint indent, const char *fmt, ...) +{ + va_list args; + char real_fmt[1000]; + int i; + + g_return_if_fail (fmt != NULL); + g_return_if_fail (indent < sizeof (real_fmt) - 2 - strlen (fmt)); + + for (i = 0; i < indent; i++) + real_fmt[i] = ' '; + strcpy (&real_fmt[i], fmt); + real_fmt[i + strlen (fmt)] = '\n'; + real_fmt[i + strlen (fmt) + 1] = '\0'; + + va_start (args, fmt); + vprintf (real_fmt, args); + va_end (args); +} + +static void +dump_device_and_parent (GUdevDevice *device, guint indent) +{ + const char **list, **iter; + GUdevDevice *parent; + char propstr[500]; + guint32 namelen = 0, i; + + println (indent, "------------------------------------------------------"); + println (indent, "Name: %s", g_udev_device_get_name (device)); + println (indent, "Type: %s", g_udev_device_get_devtype (device)); + println (indent, "Subsys: %s", g_udev_device_get_subsystem (device)); + println (indent, "Number: %s", g_udev_device_get_number (device)); + println (indent, "Path: %s", g_udev_device_get_sysfs_path (device)); + println (indent, "Driver: %s", g_udev_device_get_driver (device)); + println (indent, "Action: %s", g_udev_device_get_action (device)); + println (indent, "Seq Num: %s", g_udev_device_get_seqnum (device)); + println (indent, "Dev File: %s", g_udev_device_get_device_file (device)); + + println (indent, ""); + println (indent, "Properties:"); + + /* Get longest property name length for alignment */ + list = (const char **) g_udev_device_get_property_keys (device); + for (iter = list; iter && *iter; iter++) { + if (strlen (*iter) > namelen) + namelen = strlen (*iter); + } + namelen++; + + for (iter = list; iter && *iter; iter++) { + strcpy (propstr, *iter); + strcat (propstr, ":"); + for (i = 0; i < namelen - strlen (*iter); i++) + strcat (propstr, " "); + strcat (propstr, g_udev_device_get_property (device, *iter)); + println (indent + 2, "%s", propstr); + } + + println (indent, ""); + + parent = g_udev_device_get_parent (device); + if (parent) { + dump_device_and_parent (parent, indent + 4); + g_object_unref (parent); + } +} + +static void +handle_uevent (GUdevClient *client, + const char *action, + GUdevDevice *device, + gpointer user_data) +{ + const char *expected_subsys = user_data; + const char *subsys; + + g_return_if_fail (client != NULL); + g_return_if_fail (action != NULL); + g_return_if_fail (device != NULL); + + /* A bit paranoid */ + subsys = g_udev_device_get_subsystem (device); + g_return_if_fail (subsys != NULL); + + g_return_if_fail (!strcmp (subsys, expected_subsys)); + + g_print ("---- (EVENT: %s) ----\n", action); + dump_device_and_parent (device, 0); + g_print ("\n"); +} + +int +main (int argc, char *argv[]) +{ + GUdevClient *client; + const char *subsys[2] = { NULL, NULL }; + GList *list, *iter; + + if (argc != 2) { + g_warning ("Usage: %s [subsystem]", argv[0]); + return 1; + } + + g_type_init (); + + loop = g_main_loop_new (NULL, FALSE); + + setup_signals (); + + subsys[0] = argv[1]; + client = g_udev_client_new (subsys); + g_signal_connect (client, "uevent", G_CALLBACK (handle_uevent), (gpointer) subsys[0]); + + list = g_udev_client_query_by_subsystem (client, subsys[0]); + for (iter = list; iter; iter = g_list_next (iter)) { + dump_device_and_parent (G_UDEV_DEVICE (iter->data), 0); + g_print ("\n"); + g_object_unref (G_UDEV_DEVICE (iter->data)); + } + + g_main_loop_run (loop); + + return 0; +} + diff --git a/test/mm-send-sms.py b/test/mm-send-sms.py new file mode 100755 index 0000000..55d453c --- /dev/null +++ b/test/mm-send-sms.py @@ -0,0 +1,52 @@ +#!/usr/bin/python +# +# 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) 2009 Novell, Inc. +# + +# An example on how to send an SMS message using ModemManager + +import sys +import dbus + +if len(sys.argv) != 3: + print "Usage: %s <number> <message>" % sys.argv[0] + sys.exit(1) + +number = sys.argv[1] +message = sys.argv[2] + +bus = dbus.SystemBus() + +manager_proxy = bus.get_object('org.freedesktop.ModemManager', '/org/freedesktop/ModemManager') +manager_iface = dbus.Interface(manager_proxy, dbus_interface='org.freedesktop.ModemManager') +modems = manager_iface.EnumerateDevices() +if len(modems) == 0: + print "No modems found" + sys.exit(1) + +proxy = bus.get_object('org.freedesktop.ModemManager', modems[0]) +modem = dbus.Interface(proxy, dbus_interface='org.freedesktop.ModemManager.Modem') +modem.Enable(True) + +msg_dict = dbus.Dictionary({ dbus.String('number') : dbus.String(number), + dbus.String('text') : dbus.String(message) + }, + signature=dbus.Signature("sv")) + +sms_iface = dbus.Interface(proxy, dbus_interface='org.freedesktop.ModemManager.Modem.Gsm.SMS') +try: + sms_iface.Send(msg_dict) +except: + print "Sending message failed" +finally: + modem.Enable(False) diff --git a/test/mm-test-pppd-plugin.c b/test/mm-test-pppd-plugin.c new file mode 100644 index 0000000..75163fc --- /dev/null +++ b/test/mm-test-pppd-plugin.c @@ -0,0 +1,264 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; 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. + * + * 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., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright (C) 2008 Novell, Inc. + * Copyright (C) 2008 - 2009 Red Hat, Inc. + */ + +#include <string.h> +#include <pppd/pppd.h> +#include <pppd/fsm.h> +#include <pppd/ipcp.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <glib.h> + +int plugin_init (void); + +char pppd_version[] = VERSION; +char *my_user = NULL; +char *my_pass = NULL; +char *my_file = NULL; + +static void +mm_phasechange (void *data, int arg) +{ + const char *ppp_phase = NULL; + + switch (arg) { + case PHASE_DEAD: + ppp_phase = "dead"; + break; + case PHASE_INITIALIZE: + ppp_phase = "initialize"; + break; + case PHASE_SERIALCONN: + ppp_phase = "serial connection"; + break; + case PHASE_DORMANT: + ppp_phase = "dormant"; + break; + case PHASE_ESTABLISH: + ppp_phase = "establish"; + break; + case PHASE_AUTHENTICATE: + ppp_phase = "authenticate"; + break; + case PHASE_CALLBACK: + ppp_phase = "callback"; + break; + case PHASE_NETWORK: + ppp_phase = "network"; + break; + case PHASE_RUNNING: + ppp_phase = "running"; + break; + case PHASE_TERMINATE: + ppp_phase = "terminate"; + break; + case PHASE_DISCONNECT: + ppp_phase = "disconnect"; + break; + case PHASE_HOLDOFF: + ppp_phase = "holdoff"; + break; + case PHASE_MASTER: + ppp_phase = "master"; + break; + default: + ppp_phase = "unknown"; + break; + } + + g_message ("mm-test-ppp-plugin: (%s): phase now '%s'", __func__, ppp_phase); +} + +static void +append_ip4_addr (GString *str, const char *tag, guint32 addr) +{ + char buf[INET_ADDRSTRLEN + 2]; + struct in_addr tmp_addr = { .s_addr = addr }; + + memset (buf, 0, sizeof (buf)); + + if (inet_ntop (AF_INET, &tmp_addr, buf, sizeof (buf) - 1)) + g_string_append_printf (str, "%s %s\n", tag, buf); +} + +static void +mm_ip_up (void *data, int arg) +{ + ipcp_options opts = ipcp_gotoptions[0]; + ipcp_options peer_opts = ipcp_hisoptions[0]; + guint32 pppd_made_up_address = htonl (0x0a404040 + ifunit); + GString *contents; + GError *err = NULL; + gboolean success; + + g_message ("mm-test-ppp-plugin: (%s): ip-up event", __func__); + + if (!opts.ouraddr) { + g_warning ("mm-test-ppp-plugin: (%s): didn't receive an internal IP from pppd!", __func__); + mm_phasechange (NULL, PHASE_DEAD); + return; + } + + contents = g_string_sized_new (100); + + g_string_append_printf (contents, "iface %s\n", ifname); + + append_ip4_addr (contents, "addr", opts.ouraddr); + + /* Prefer the peer options remote address first, _unless_ pppd made the + * address up, at which point prefer the local options remote address, + * and if that's not right, use the made-up address as a last resort. + */ + if (peer_opts.hisaddr && (peer_opts.hisaddr != pppd_made_up_address)) + append_ip4_addr (contents, "gateway", peer_opts.hisaddr); + else if (opts.hisaddr) + append_ip4_addr (contents, "gateway", opts.hisaddr); + else if (peer_opts.hisaddr == pppd_made_up_address) { + /* As a last resort, use the made-up address */ + append_ip4_addr (contents, "gateway", peer_opts.hisaddr); + } + + if (opts.dnsaddr[0] || opts.dnsaddr[1]) { + if (opts.dnsaddr[0]) + append_ip4_addr (contents, "dns1", opts.dnsaddr[0]); + if (opts.dnsaddr[1]) + append_ip4_addr (contents, "dns2", opts.dnsaddr[1]); + } + + if (opts.winsaddr[0] || opts.winsaddr[1]) { + if (opts.winsaddr[0]) + append_ip4_addr (contents, "wins1", opts.winsaddr[0]); + if (opts.winsaddr[1]) + append_ip4_addr (contents, "wins2", opts.winsaddr[1]); + } + + g_string_append (contents, "DONE\n"); + + success = g_file_set_contents (my_file, contents->str, -1, &err); + if (success) + g_message ("nm-ppp-plugin: (%s): saved IP4 config to %s", __func__, my_file); + else { + g_message ("nm-ppp-plugin: (%s): error saving IP4 config to %s: (%d) %s", + __func__, my_file, err->code, err->message); + g_clear_error (&err); + } + + g_string_free (contents, TRUE); +} + +static int +get_chap_check() +{ + return 1; +} + +static int +get_pap_check() +{ + return 1; +} + +static int +get_credentials (char *username, char *password) +{ + size_t len; + + if (username && !password) { + /* pppd is checking pap support; return 1 for supported */ + return 1; + } + + g_message ("nm-ppp-plugin: (%s): sending credentials (%s / %s)", + __func__, + my_user ? my_user : "", + my_pass ? my_pass : ""); + + if (my_user) { + len = strlen (my_user) + 1; + len = len < MAXNAMELEN ? len : MAXNAMELEN; + + strncpy (username, my_user, len); + username[len - 1] = '\0'; + } + + if (my_pass) { + len = strlen (my_pass) + 1; + len = len < MAXSECRETLEN ? len : MAXSECRETLEN; + + strncpy (password, my_pass, len); + password[len - 1] = '\0'; + } + + return 1; +} + +static void +mm_exit_notify (void *data, int arg) +{ + g_message ("mm-test-ppp-plugin: (%s): cleaning up", __func__); + + g_free (my_user); + my_user = NULL; + g_free (my_pass); + my_pass = NULL; + g_free (my_file); + my_file = NULL; +} + +int +plugin_init (void) +{ + char **args; + + g_message ("mm-test-ppp-plugin: (%s): initializing", __func__); + + /* mm-test passes the file + username + password in as the 'ipparam' arg + * to pppd. + */ + args = g_strsplit (ipparam, "+", 0); + if (!args || g_strv_length (args) != 3) { + g_message ("mm-test-ppp-plugin: (%s): ipparam arguments error ('%s')", + __func__, ipparam); + return -1; + } + + my_user = (args[0] && strlen (args[0])) ? g_strdup (args[0]) : NULL; + my_pass = (args[1] && strlen (args[1])) ? g_strdup (args[1]) : NULL; + my_file = (args[2] && strlen (args[2])) ? g_strdup (args[2]) : NULL; + + g_strfreev (args); + + if (!my_file) { + g_message ("mm-test-ppp-plugin: (%s): missing IP config file", + __func__); + return -1; + } + + chap_passwd_hook = get_credentials; + chap_check_hook = get_chap_check; + pap_passwd_hook = get_credentials; + pap_check_hook = get_pap_check; + + add_notifier (&phasechange, mm_phasechange, NULL); + add_notifier (&ip_up_notifier, mm_ip_up, NULL); + add_notifier (&exitnotify, mm_exit_notify, NULL); + + return 0; +} diff --git a/test/mm-test.py b/test/mm-test.py new file mode 100755 index 0000000..99a355f --- /dev/null +++ b/test/mm-test.py @@ -0,0 +1,527 @@ +#!/usr/bin/python +# -*- Mode: python; 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 Novell, Inc. +# Copyright (C) 2009 Red Hat, Inc. +# + +import sys, dbus, time, os, string, subprocess, socket + +DBUS_INTERFACE_PROPERTIES='org.freedesktop.DBus.Properties' +MM_DBUS_SERVICE='org.freedesktop.ModemManager' +MM_DBUS_PATH='/org/freedesktop/ModemManager' +MM_DBUS_INTERFACE='org.freedesktop.ModemManager' +MM_DBUS_INTERFACE_MODEM='org.freedesktop.ModemManager.Modem' +MM_DBUS_INTERFACE_MODEM_CDMA='org.freedesktop.ModemManager.Modem.Cdma' +MM_DBUS_INTERFACE_MODEM_GSM_CARD='org.freedesktop.ModemManager.Modem.Gsm.Card' +MM_DBUS_INTERFACE_MODEM_GSM_NETWORK='org.freedesktop.ModemManager.Modem.Gsm.Network' +MM_DBUS_INTERFACE_MODEM_SIMPLE='org.freedesktop.ModemManager.Modem.Simple' + +def get_cdma_band_class(band_class): + if band_class == 1: + return "800MHz" + elif band_class == 2: + return "1900MHz" + else: + return "Unknown" + +def get_reg_state(state): + if state == 1: + return "registered (roaming unknown)" + elif state == 2: + return "registered on home network" + elif state == 3: + return "registered on roaming network" + else: + return "unknown" + +def cdma_inspect(proxy, dump_private): + cdma = dbus.Interface(proxy, dbus_interface=MM_DBUS_INTERFACE_MODEM_CDMA) + + esn = "<private>" + if dump_private: + try: + esn = cdma.GetEsn() + except dbus.exceptions.DBusException: + esn = "<unavailable>" + + print "" + print "ESN: %s" % esn + + try: + (cdma_1x_state, evdo_state) = cdma.GetRegistrationState() + print "1x State: %s" % get_reg_state (cdma_1x_state) + print "EVDO State: %s" % get_reg_state (evdo_state) + except dbus.exceptions.DBusException, e: + print "Error reading registration state: %s" % e + + try: + quality = cdma.GetSignalQuality() + print "Signal quality: %d" % quality + except dbus.exceptions.DBusException, e: + print "Error reading signal quality: %s" % e + + try: + info = cdma.GetServingSystem() + print "Class: %s" % get_cdma_band_class(info[0]) + print "Band: %s" % info[1] + print "SID: %d" % info[2] + except dbus.exceptions.DBusException, e: + print "Error reading serving system: %s" % e + +def cdma_connect(proxy, user, password): + # Modem.Simple interface + simple = dbus.Interface(proxy, dbus_interface=MM_DBUS_INTERFACE_MODEM_SIMPLE) + try: + simple.Connect({'number':"#777"}, timeout=92) + print "\nConnected!" + return True + except Exception, e: + print "Error connecting: %s" % e + return False + + +def get_gsm_network_mode(modem): + mode = modem.GetNetworkMode() + if mode == 0x0: + mode = "Unknown" + elif mode == 0x1: + mode = "Any" + elif mode == 0x2: + mode = "GPRS" + elif mode == 0x4: + mode = "EDGE" + elif mode == 0x8: + mode = "UMTS" + elif mode == 0x10: + mode = "HSDPA" + elif mode == 0x20: + mode = "2G Preferred" + elif mode == 0x40: + mode = "3G Preferred" + elif mode == 0x80: + mode = "2G Only" + elif mode == 0x100: + mode = "3G Only" + elif mode == 0x200: + mode = "HSUPA" + elif mode == 0x400: + mode = "HSPA" + else: + mode = "(Unknown)" + + print "Mode: %s" % mode + +def get_gsm_band(modem): + band = modem.GetBand() + if band == 0x0: + band = "Unknown" + elif band == 0x1: + band = "Any" + elif band == 0x2: + band = "EGSM (900 MHz)" + elif band == 0x4: + band = "DCS (1800 MHz)" + elif band == 0x8: + band = "PCS (1900 MHz)" + elif band == 0x10: + band = "G850 (850 MHz)" + elif band == 0x20: + band = "U2100 (WCSMA 2100 MHZ, Class I)" + elif band == 0x40: + band = "U1700 (WCDMA 3GPP UMTS1800 MHz, Class III)" + elif band == 0x80: + band = "17IV (WCDMA 3GPP AWS 1700/2100 MHz, Class IV)" + elif band == 0x100: + band = "U800 (WCDMA 3GPP UMTS800 MHz, Class VI)" + elif band == 0x200: + band = "U850 (WCDMA 3GPP UMT850 MHz, Class V)" + elif band == 0x400: + band = "U900 (WCDMA 3GPP UMTS900 MHz, Class VIII)" + elif band == 0x800: + band = "U17IX (WCDMA 3GPP UMTS MHz, Class IX)" + else: + band = "(invalid)" + + print "Band: %s" % band + + +def gsm_inspect(proxy, dump_private, do_scan): + # Gsm.Card interface + card = dbus.Interface(proxy, dbus_interface=MM_DBUS_INTERFACE_MODEM_GSM_CARD) + + imei = "<private>" + imsi = "<private>" + if dump_private: + try: + imei = card.GetImei() + except dbus.exceptions.DBusException: + imei = "<unavailable>" + try: + imsi = card.GetImsi() + except dbus.exceptions.DBusException: + imsi = "<unavailable>" + + print "IMEI: %s" % imei + print "IMSI: %s" % imsi + + # Gsm.Network interface + net = dbus.Interface(proxy, dbus_interface=MM_DBUS_INTERFACE_MODEM_GSM_NETWORK) + try: + quality = net.GetSignalQuality() + print "Signal quality: %d" % quality + except dbus.exceptions.DBusException, e: + print "Error reading signal quality: %s" % e + + if not do_scan: + return + + print "Scanning..." + try: + results = net.Scan(timeout=120) + except dbus.exceptions.DBusException, e: + print "Error scanning: %s" % e + results = {} + + for r in results: + status = r['status'] + if status == "1": + status = "available" + elif status == "2": + status = "current" + elif status == "3": + status = "forbidden" + else: + status = "(Unknown)" + + access_tech = "" + try: + access_tech_num = r['access-tech'] + if access_tech_num == "0": + access_tech = "(GSM)" + elif access_tech_num == "1": + access_tech = "(Compact GSM)" + elif access_tech_num == "2": + access_tech = "(UMTS)" + elif access_tech_num == "3": + access_tech = "(EDGE)" + elif access_tech_num == "4": + access_tech = "(HSDPA)" + elif access_tech_num == "5": + access_tech = "(HSUPA)" + elif access_tech_num == "6": + access_tech = "(HSPA)" + except KeyError: + pass + + if r.has_key('operator-long') and len(r['operator-long']): + print "%s: %s %s" % (r['operator-long'], status, access_tech) + elif r.has_key('operator-short') and len(r['operator-short']): + print "%s: %s %s" % (r['operator-short'], status, access_tech) + else: + print "%s: %s %s" % (r['operator-num'], status, access_tech) + +def gsm_connect(proxy, apn, user, password): + # Modem.Simple interface + simple = dbus.Interface(proxy, dbus_interface=MM_DBUS_INTERFACE_MODEM_SIMPLE) + try: + opts = {'number':"*99#"} + if apn is not None: + opts['apn'] = apn + if user is not None: + opts['username'] = user + if password is not None: + opts['password'] = password + simple.Connect(opts, timeout=120) + print "\nConnected!" + return True + except Exception, e: + print "Error connecting: %s" % e + return False + +def pppd_find(): + paths = ["/usr/local/sbin/pppd", "/usr/sbin/pppd", "/sbin/pppd"] + for p in paths: + if os.path.exists(p): + return p + return None + +def ppp_start(device, user, password, tmpfile): + path = pppd_find() + if not path: + return None + + args = [path] + args += ["nodetach"] + args += ["lock"] + args += ["nodefaultroute"] + args += ["debug"] + if user: + args += ["user"] + args += [user] + args += ["noipdefault"] + args += ["115200"] + args += ["noauth"] + args += ["crtscts"] + args += ["modem"] + args += ["usepeerdns"] + args += ["ipparam"] + + ipparam = "" + if user: + ipparam += user + ipparam += "+" + if password: + ipparam += password + ipparam += "+" + ipparam += tmpfile + args += [ipparam] + + args += ["plugin"] + args += ["mm-test-pppd-plugin.so"] + + args += [device] + + return subprocess.Popen(args, close_fds=True, cwd="/", env={}) + +def ppp_wait(p, tmpfile): + i = 0 + while p.poll() == None and i < 30: + time.sleep(1) + if os.path.exists(tmpfile): + f = open(tmpfile, 'r') + stuff = f.read(500) + idx = string.find(stuff, "DONE") + f.close() + if idx >= 0: + return True + i += 1 + return False + +def ppp_stop(p): + import signal + p.send_signal(signal.SIGTERM) + p.wait() + +def ntop_helper(ip): + ip = socket.ntohl(ip) + n1 = ip >> 24 & 0xFF + n2 = ip >> 16 & 0xFF + n3 = ip >> 8 & 0xFF + n4 = ip & 0xFF + a = "%c%c%c%c" % (n1, n2, n3, n4) + return socket.inet_ntop(socket.AF_INET, a) + +def static_start(iface, modem): + (addr_num, dns1_num, dns2_num, dns3_num) = modem.GetIP4Config() + addr = ntop_helper(addr_num) + dns1 = ntop_helper(dns1_num) + dns2 = ntop_helper(dns2_num) + configure_iface(iface, addr, 0, dns1, dns2) + +def down_iface(iface): + ip = ["ip", "addr", "flush", "dev", iface] + print " ".join(ip) + subprocess.call(ip) + ip = ["ip", "link", "set", iface, "down"] + print " ".join(ip) + subprocess.call(ip) + +def configure_iface(iface, addr, gw, dns1, dns2): + print "\n\n******************************" + print "iface: %s" % iface + print "addr: %s" % addr + print "gw: %s" % gw + print "dns1: %s" % dns1 + print "dns2: %s" % dns2 + + ifconfig = ["ifconfig", iface, "%s/32" % addr] + if gw != 0: + ifconfig += ["pointopoint", gw] + print " ".join(ifconfig) + print "\n******************************\n" + + subprocess.call(ifconfig) + +def file_configure_iface(tmpfile): + addr = None + gw = None + iface = None + dns1 = None + dns2 = None + + f = open(tmpfile, 'r') + lines = f.readlines() + for l in lines: + if l.startswith("addr"): + addr = l[len("addr"):].strip() + if l.startswith("gateway"): + gw = l[len("gateway"):].strip() + if l.startswith("iface"): + iface = l[len("iface"):].strip() + if l.startswith("dns1"): + dns1 = l[len("dns1"):].strip() + if l.startswith("dns2"): + dns2 = l[len("dns2"):].strip() + f.close() + + configure_iface(iface, addr, gw, dns1, dns2) + return iface + +def try_ping(iface): + cmd = ["ping", "-I", iface, "-c", "4", "-i", "3", "-w", "20", "4.2.2.1"] + print " ".join(cmd) + retcode = subprocess.call(cmd) + if retcode != 0: + print "PING: failed" + else: + print "PING: success" + + +dump_private = False +connect = False +apn = None +user = None +password = None +do_ip = False +do_scan = True +x = 1 +while x < len(sys.argv): + if sys.argv[x] == "--private": + dump_private = True + elif sys.argv[x] == "--connect": + connect = True + elif (sys.argv[x] == "--user" or sys.argv[x] == "--username"): + x += 1 + user = sys.argv[x] + elif sys.argv[x] == "--apn": + x += 1 + apn = sys.argv[x] + elif sys.argv[x] == "--password": + x += 1 + password = sys.argv[x] + elif sys.argv[x] == "--ip": + do_ip = True + if os.geteuid() != 0: + print "You probably want to be root to use --ip" + sys.exit(1) + elif sys.argv[x] == "--no-scan": + do_scan = False + x += 1 + +bus = dbus.SystemBus() + +# Get available modems: +manager_proxy = bus.get_object('org.freedesktop.ModemManager', '/org/freedesktop/ModemManager') +manager_iface = dbus.Interface(manager_proxy, dbus_interface='org.freedesktop.ModemManager') +modems = manager_iface.EnumerateDevices() + +if not modems: + print "No modems found" + sys.exit(1) + +for m in modems: + connect_success = False + data_device = None + + proxy = bus.get_object(MM_DBUS_SERVICE, m) + + # Properties + props_iface = dbus.Interface(proxy, dbus_interface='org.freedesktop.DBus.Properties') + + type = props_iface.Get(MM_DBUS_INTERFACE_MODEM, 'Type') + if type == 1: + print "GSM modem" + elif type == 2: + print "CDMA modem" + else: + print "Invalid modem type: %d" % type + + print "Driver: '%s'" % (props_iface.Get(MM_DBUS_INTERFACE_MODEM, 'Driver')) + print "Modem device: '%s'" % (props_iface.Get(MM_DBUS_INTERFACE_MODEM, 'MasterDevice')) + data_device = props_iface.Get(MM_DBUS_INTERFACE_MODEM, 'Device') + print "Data device: '%s'" % data_device + + # Modem interface + modem = dbus.Interface(proxy, dbus_interface=MM_DBUS_INTERFACE_MODEM) + + try: + modem.Enable(True) + except dbus.exceptions.DBusException, e: + print "Error enabling modem: %s" % e + sys.exit(1) + + info = modem.GetInfo() + print "Vendor: %s" % info[0] + print "Model: %s" % info[1] + print "Version: %s" % info[2] + + if type == 1: + gsm_inspect(proxy, dump_private, do_scan) + if connect == True: + connect_success = gsm_connect(proxy, apn, user, password) + elif type == 2: + cdma_inspect(proxy, dump_private) + if connect == True: + connect_success = cdma_connect(proxy, user, password) + print + + if connect_success and do_ip: + tmpfile = "/tmp/mm-test-%d.tmp" % os.getpid() + success = False + try: + ip_method = props_iface.Get(MM_DBUS_INTERFACE_MODEM, 'IpMethod') + if ip_method == 0: + # ppp + p = ppp_start(data_device, user, password, tmpfile) + if ppp_wait(p, tmpfile): + data_device = file_configure_iface(tmpfile) + success = True + elif ip_method == 1: + # static + static_start(data_device, modem) + success = True + elif ip_method == 2: + # dhcp + pass + except Exception, e: + print "Error setting up IP: %s" % e + + if success: + try_ping(data_device) + print "Waiting for 30s..." + time.sleep(30) + + print "Disconnecting..." + try: + if ip_method == 0: + ppp_stop(p) + try: + os.remove(tmpfile) + except: + pass + elif ip_method == 1: + # static + down_iface(data_device) + elif ip_method == 2: + # dhcp + down_iface(data_device) + + modem.Disconnect() + except Exception, e: + print "Error tearing down IP: %s" % e + + time.sleep(5) + + modem.Enable(False) + |