aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGuido Günther <agx@sigxcpu.org>2014-02-05 08:38:15 +0100
committerGuido Günther <agx@sigxcpu.org>2014-02-05 08:38:15 +0100
commit87bd9deec22af69bb27226254803ac5c63b18d78 (patch)
treec34d42bf75c20b3fd740e4cd59e45aa6901a9fed
Imported Upstream version 0.3upstream/0.3
-rw-r--r--.gitignore29
-rw-r--r--AUTHORS2
-rw-r--r--COPYING340
-rw-r--r--ChangeLog0
-rw-r--r--Makefile.am49
-rw-r--r--NEWS0
-rw-r--r--README34
-rwxr-xr-xautogen.sh20
-rw-r--r--configure.ac133
-rw-r--r--doc-generator.xsl691
-rw-r--r--docs/plugins.txt0
-rw-r--r--introspection/Makefile.am16
-rw-r--r--introspection/all.xml40
-rw-r--r--introspection/mm-manager.xml40
-rw-r--r--introspection/mm-mobile-error.xml318
-rw-r--r--introspection/mm-modem-cdma.xml97
-rw-r--r--introspection/mm-modem-connect-error.xml30
-rw-r--r--introspection/mm-modem-error.xml36
-rw-r--r--introspection/mm-modem-gsm-card.xml107
-rw-r--r--introspection/mm-modem-gsm-contacts.xml104
-rw-r--r--introspection/mm-modem-gsm-hso.xml19
-rw-r--r--introspection/mm-modem-gsm-network.xml202
-rw-r--r--introspection/mm-modem-gsm-sms.xml156
-rw-r--r--introspection/mm-modem-gsm.xml86
-rw-r--r--introspection/mm-modem-simple.xml58
-rw-r--r--introspection/mm-modem.xml154
-rw-r--r--introspection/mm-serial-error.xml24
-rw-r--r--marshallers/Makefile.am21
-rw-r--r--marshallers/mm-marshal-main.c2
-rw-r--r--marshallers/mm-marshal.list8
-rw-r--r--obsolete-patches/NetworkManager-r4359-use-modem-manager.patch5691
-rw-r--r--obsolete-patches/nm-applet-r1053-use-modem-manager.patch2253
-rw-r--r--org.freedesktop.ModemManager.conf14
-rw-r--r--org.freedesktop.ModemManager.service.in4
-rw-r--r--plugins/77-mm-ericsson-mbm.rules41
-rw-r--r--plugins/77-mm-longcheer-port-types.rules45
-rw-r--r--plugins/77-mm-zte-port-types.rules115
-rw-r--r--plugins/Makefile.am268
-rw-r--r--plugins/mm-modem-anydata-cdma.c384
-rw-r--r--plugins/mm-modem-anydata-cdma.h45
-rw-r--r--plugins/mm-modem-gobi-gsm.c109
-rw-r--r--plugins/mm-modem-gobi-gsm.h43
-rw-r--r--plugins/mm-modem-hso.c743
-rw-r--r--plugins/mm-modem-hso.h49
-rw-r--r--plugins/mm-modem-huawei-cdma.c316
-rw-r--r--plugins/mm-modem-huawei-cdma.h45
-rw-r--r--plugins/mm-modem-huawei-gsm.c621
-rw-r--r--plugins/mm-modem-huawei-gsm.h43
-rw-r--r--plugins/mm-modem-mbm.c830
-rw-r--r--plugins/mm-modem-mbm.h52
-rw-r--r--plugins/mm-modem-moto-c-gsm.c129
-rw-r--r--plugins/mm-modem-moto-c-gsm.h43
-rw-r--r--plugins/mm-modem-nokia.c142
-rw-r--r--plugins/mm-modem-nokia.h43
-rw-r--r--plugins/mm-modem-novatel-gsm.c184
-rw-r--r--plugins/mm-modem-novatel-gsm.h43
-rw-r--r--plugins/mm-modem-option.c244
-rw-r--r--plugins/mm-modem-option.h43
-rw-r--r--plugins/mm-modem-sierra-cdma.c383
-rw-r--r--plugins/mm-modem-sierra-cdma.h45
-rw-r--r--plugins/mm-modem-sierra-gsm.c145
-rw-r--r--plugins/mm-modem-sierra-gsm.h43
-rw-r--r--plugins/mm-modem-zte.c235
-rw-r--r--plugins/mm-modem-zte.h43
-rw-r--r--plugins/mm-plugin-anydata.c179
-rw-r--r--plugins/mm-plugin-anydata.h41
-rw-r--r--plugins/mm-plugin-generic.c189
-rw-r--r--plugins/mm-plugin-generic.h41
-rw-r--r--plugins/mm-plugin-gobi.c171
-rw-r--r--plugins/mm-plugin-gobi.h44
-rw-r--r--plugins/mm-plugin-hso.c182
-rw-r--r--plugins/mm-plugin-hso.h43
-rw-r--r--plugins/mm-plugin-huawei.c341
-rw-r--r--plugins/mm-plugin-huawei.h42
-rw-r--r--plugins/mm-plugin-longcheer.c191
-rw-r--r--plugins/mm-plugin-longcheer.h41
-rw-r--r--plugins/mm-plugin-mbm.c175
-rw-r--r--plugins/mm-plugin-mbm.h48
-rw-r--r--plugins/mm-plugin-moto-c.c160
-rw-r--r--plugins/mm-plugin-moto-c.h43
-rw-r--r--plugins/mm-plugin-nokia.c179
-rw-r--r--plugins/mm-plugin-nokia.h41
-rw-r--r--plugins/mm-plugin-novatel.c183
-rw-r--r--plugins/mm-plugin-novatel.h42
-rw-r--r--plugins/mm-plugin-option.c180
-rw-r--r--plugins/mm-plugin-option.h42
-rw-r--r--plugins/mm-plugin-sierra.c204
-rw-r--r--plugins/mm-plugin-sierra.h42
-rw-r--r--plugins/mm-plugin-zte.c189
-rw-r--r--plugins/mm-plugin-zte.h41
-rw-r--r--src/Makefile.am99
-rw-r--r--src/main.c211
-rw-r--r--src/mm-callback-info.c210
-rw-r--r--src/mm-callback-info.h74
-rw-r--r--src/mm-errors.c290
-rw-r--r--src/mm-errors.h131
-rw-r--r--src/mm-generic-cdma.c1831
-rw-r--r--src/mm-generic-cdma.h111
-rw-r--r--src/mm-generic-gsm.c2220
-rw-r--r--src/mm-generic-gsm.h129
-rw-r--r--src/mm-manager.c714
-rw-r--r--src/mm-manager.h53
-rw-r--r--src/mm-modem-base.c341
-rw-r--r--src/mm-modem-base.h64
-rw-r--r--src/mm-modem-cdma.c346
-rw-r--r--src/mm-modem-cdma.h108
-rw-r--r--src/mm-modem-gsm-card.c312
-rw-r--r--src/mm-modem-gsm-card.h101
-rw-r--r--src/mm-modem-gsm-network.c569
-rw-r--r--src/mm-modem-gsm-network.h164
-rw-r--r--src/mm-modem-gsm-sms.c320
-rw-r--r--src/mm-modem-gsm-sms.h62
-rw-r--r--src/mm-modem-gsm.h57
-rw-r--r--src/mm-modem-helpers.c203
-rw-r--r--src/mm-modem-helpers.h31
-rw-r--r--src/mm-modem-simple.c156
-rw-r--r--src/mm-modem-simple.h59
-rw-r--r--src/mm-modem.c738
-rw-r--r--src/mm-modem.h219
-rw-r--r--src/mm-options.c55
-rw-r--r--src/mm-options.h23
-rw-r--r--src/mm-plugin-base.c1126
-rw-r--r--src/mm-plugin-base.h150
-rw-r--r--src/mm-plugin.c100
-rw-r--r--src/mm-plugin.h120
-rw-r--r--src/mm-port.c278
-rw-r--r--src/mm-port.h79
-rw-r--r--src/mm-properties-changed-signal.c276
-rw-r--r--src/mm-properties-changed-signal.h28
-rw-r--r--src/mm-serial-parsers.c372
-rw-r--r--src/mm-serial-parsers.h44
-rw-r--r--src/mm-serial-port.c1281
-rw-r--r--src/mm-serial-port.h105
-rw-r--r--src/tests/Makefile.am22
-rw-r--r--src/tests/test-modem-helpers.c479
-rw-r--r--test/Makefile.am23
-rw-r--r--test/lsudev.c180
-rwxr-xr-xtest/mm-send-sms.py52
-rw-r--r--test/mm-test-pppd-plugin.c264
-rwxr-xr-xtest/mm-test.py527
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
+
diff --git a/AUTHORS b/AUTHORS
new file mode 100644
index 0000000..12af19c
--- /dev/null
+++ b/AUTHORS
@@ -0,0 +1,2 @@
+Tambet Ingo <tambet@gmail.com>
+
diff --git a/COPYING b/COPYING
new file mode 100644
index 0000000..d60c31a
--- /dev/null
+++ b/COPYING
@@ -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)
diff --git a/NEWS b/NEWS
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/NEWS
diff --git a/README b/README
new file mode 100644
index 0000000..e8f2f4f
--- /dev/null
+++ b/README
@@ -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()"> &#x2192; </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()"> &#x2192; </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>
+ ) &#x2192;
+ <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>'&#10;</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">&lt;b&gt;Information&lt;/b&gt;</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">&lt;b&gt;PIN operations&lt;/b&gt;</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">&lt;b&gt;Networks&lt;/b&gt;</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)
+